Metadata-Version: 2.4
Name: azure-functions-langgraph
Version: 0.5.4
Summary: LangGraph integration for Azure Functions Python v2 — deploy compiled graphs as HTTP endpoints
Project-URL: Homepage, https://yeongseon.github.io/azure-functions-langgraph-python/
Project-URL: Documentation, https://yeongseon.github.io/azure-functions-langgraph-python/
Project-URL: Repository, https://github.com/yeongseon/azure-functions-langgraph-python
Project-URL: Issues, https://github.com/yeongseon/azure-functions-langgraph-python/issues
Author-email: Yeongseon Choe <yeongseon.choe@gmail.com>
License: MIT
License-File: LICENSE
Keywords: ai-agent,azure,azure-functions,langchain,langgraph,llm,python,serverless
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: <3.15,>=3.10
Requires-Dist: azure-functions
Requires-Dist: langgraph<2.0,>=1.0
Requires-Dist: pydantic<3.0,>=2.0
Provides-Extra: azure-blob
Requires-Dist: azure-storage-blob<13,>=12.0; extra == 'azure-blob'
Provides-Extra: azure-table
Requires-Dist: azure-data-tables<13,>=12.0; extra == 'azure-table'
Provides-Extra: dev
Requires-Dist: bandit==1.9.4; extra == 'dev'
Requires-Dist: build; extra == 'dev'
Requires-Dist: coverage; extra == 'dev'
Requires-Dist: git-cliff; extra == 'dev'
Requires-Dist: hatch; extra == 'dev'
Requires-Dist: langgraph-sdk<0.4,>=0.3; extra == 'dev'
Requires-Dist: mypy==1.20.1; extra == 'dev'
Requires-Dist: pre-commit; extra == 'dev'
Requires-Dist: pytest; extra == 'dev'
Requires-Dist: pytest-asyncio<2,>=0.23; extra == 'dev'
Requires-Dist: pytest-cov; extra == 'dev'
Requires-Dist: requests; extra == 'dev'
Requires-Dist: ruff==0.15.10; extra == 'dev'
Requires-Dist: types-requests; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs-material<10.0; extra == 'docs'
Requires-Dist: mkdocs<2.0; extra == 'docs'
Requires-Dist: mkdocstrings[python]<2.0; extra == 'docs'
Description-Content-Type: text/markdown

# Azure Functions LangGraph

[![PyPI](https://img.shields.io/pypi/v/azure-functions-langgraph.svg)](https://pypi.org/project/azure-functions-langgraph/)
[![Python Version](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue)](https://pypi.org/project/azure-functions-langgraph/)
[![CI](https://github.com/yeongseon/azure-functions-langgraph-python/actions/workflows/ci-test.yml/badge.svg)](https://github.com/yeongseon/azure-functions-langgraph-python/actions/workflows/ci-test.yml)
[![Release](https://github.com/yeongseon/azure-functions-langgraph-python/actions/workflows/publish-pypi.yml/badge.svg)](https://github.com/yeongseon/azure-functions-langgraph-python/actions/workflows/publish-pypi.yml)
[![Security Scans](https://github.com/yeongseon/azure-functions-langgraph-python/actions/workflows/security.yml/badge.svg)](https://github.com/yeongseon/azure-functions-langgraph-python/actions/workflows/security.yml)
[![codecov](https://codecov.io/gh/yeongseon/azure-functions-langgraph/branch/main/graph/badge.svg)](https://codecov.io/gh/yeongseon/azure-functions-langgraph)
[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit)](https://pre-commit.com/)
[![Docs](https://img.shields.io/badge/docs-gh--pages-blue)](https://yeongseon.github.io/azure-functions-langgraph-python/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

Read this in: [한국어](README.ko.md) | [日本語](README.ja.md) | [简体中文](README.zh-CN.md)

> **Beta Notice** — This package is under active development. Core APIs are stabilizing but may still change before v1.0. Please report issues on GitHub.

Deploy [LangGraph](https://github.com/langchain-ai/langgraph) graphs as **Azure Functions** HTTP endpoints with minimal boilerplate.

---

Part of the **Azure Functions Python DX Toolkit**

## Why this exists

Deploying LangGraph on Azure Functions is harder than it should be.

- LangGraph does not provide an Azure Functions-native deployment adapter
- Exposing compiled graphs as HTTP endpoints requires repetitive wiring
- Teams often rebuild the same invoke/stream wrapper for every project

This package provides a focused adapter for serving LangGraph graphs on Azure Functions Python v2.

## What it does

- **Zero-boilerplate deployment** — register a compiled graph, get HTTP endpoints automatically
- **Invoke endpoint** — `POST /api/graphs/{name}/invoke` for synchronous execution
- **Stream endpoint** — `POST /api/graphs/{name}/stream` for buffered SSE responses
- **Health endpoint** — `GET /api/health` listing registered graphs with checkpointer status
- **Checkpointer pass-through** — thread-based conversation state works via LangGraph's native config
- **State endpoint** — `GET /api/graphs/{name}/threads/{thread_id}/state` for thread state inspection (when supported)
- **Per-graph auth** — override app-level auth with `register(..., auth_level=...)`
- **LangGraph Platform API compatibility** — SDK-compatible endpoints for threads, runs, assistants, and state (v0.3+)
- **Persistent storage backends** — Azure Blob Storage checkpointer and Azure Table Storage thread store (v0.4+)

## LangGraph Platform comparison

| Feature | LangGraph Platform | azure-functions-langgraph |
|---------|-------------------|--------------------------|
| Hosting | LangChain Cloud (paid) | Your Azure subscription |
| Assistants | Built-in | SDK-compatible API (v0.3+) |
| Thread lifecycle | Built-in | Create, get, update, delete, search, count (v0.3+) |
| Runs | Built-in | Threaded + threadless runs (v0.4+) |
| State read/update | Built-in | get_state + update_state (v0.4+) |
| State history | Built-in | Checkpoint history with filtering (v0.4+) |
| Streaming | True SSE | Buffered SSE |
| Persistent storage | Built-in | Azure Blob + Table Storage (v0.4+) |
| Infrastructure | Managed | Azure Functions (serverless) |
| Cost model | Per-seat/usage | Azure Functions pricing |

> See [COMPATIBILITY.md](COMPATIBILITY.md) for the per-feature SDK support matrix, including which `RunCreate` fields, thread filters, and SDK calls return `501 Not Implemented`.

## Scope

- Azure Functions Python **v2 programming model**
- LangGraph graph deployment and HTTP exposure
- LangGraph runtime concerns: invoke, stream, threads, runs, and state
- Optional integration points for validation and OpenAPI via companion packages

This package is a **deployment adapter** — it wraps LangGraph, it does not replace it.

> Internally, graph registration remains protocol-based (`LangGraphLike`), so any object satisfying the protocol works — but the package's documentation and examples focus on LangGraph use cases.

## What this package does not do

This package does not own:
- OpenAPI document generation or Swagger UI — use [`azure-functions-openapi-python`](https://github.com/yeongseon/azure-functions-openapi-python)
- Request/response validation beyond LangGraph contracts — use [`azure-functions-validation-python`](https://github.com/yeongseon/azure-functions-validation-python)
- Generic graph-serving abstractions beyond LangGraph

> **Note:** For OpenAPI spec generation, use the [`azure-functions-openapi-python`](https://github.com/yeongseon/azure-functions-openapi-python) package with the bridge module (`azure_functions_langgraph.openapi.register_with_openapi`).

## Installation

```bash
pip install azure-functions-langgraph
```

For persistent storage with Azure services:

```bash
# Azure Blob Storage checkpointer
pip install azure-functions-langgraph[azure-blob]

# Azure Table Storage thread store
pip install azure-functions-langgraph[azure-table]

# Both
pip install azure-functions-langgraph[azure-blob,azure-table]
```

Your Azure Functions app should also include:

```text
azure-functions
langgraph
azure-functions-langgraph
```

For local development:

```bash
git clone https://github.com/yeongseon/azure-functions-langgraph-python.git
cd azure-functions-langgraph
pip install -e .[dev]
```

## Quick Start

```python
from langgraph.graph import END, START, StateGraph
from typing_extensions import TypedDict

import azure.functions as func

from azure_functions_langgraph import LangGraphApp


# 1. Define your state
class AgentState(TypedDict):
    messages: list[dict[str, str]]


# 2. Define your nodes
def chat(state: AgentState) -> dict:
    user_msg = state["messages"][-1]["content"]
    return {"messages": state["messages"] + [{"role": "assistant", "content": f"Echo: {user_msg}"}]}


# 3. Build graph
builder = StateGraph(AgentState)
builder.add_node("chat", chat)
builder.add_edge(START, "chat")
builder.add_edge("chat", END)
graph = builder.compile()

# 4. Deploy (ANONYMOUS for local dev; use FUNCTION in production — see below)
app = LangGraphApp(auth_level=func.AuthLevel.ANONYMOUS)
app.register(graph=graph, name="echo_agent")
func_app = app.function_app  # ← use this as your Azure Functions app
```

Start the Functions host locally:

```bash
func start
```

### Verify locally and on Azure

After deploying (see [docs/deployment.md](docs/deployment.md)), the same request produces the same response in both environments. Azure requires a function key (`?code=<FUNCTION_KEY>`) when `auth_level` is set to `FUNCTION`.

#### Local

```bash
curl -s http://localhost:7071/api/health
```

```json
{"status": "ok", "graphs": [{"name": "echo_agent", "description": null, "has_checkpointer": false}]}
```

#### Azure

```bash
curl -s "https://<your-app>.azurewebsites.net/api/health?code=<FUNCTION_KEY>"
```

```json
{"status": "ok", "graphs": [{"name": "echo_agent", "description": null, "has_checkpointer": false}]}
```

> Response format verified against a temporary Azure Functions deployment of the `simple_agent` example in koreacentral (Python 3.12, Consumption plan). The Quick Start uses `echo_agent` for illustration; the health endpoint returns the same JSON structure regardless of graph name. URL anonymized.


### Production authentication

> **Important:** `LangGraphApp` defaults to `AuthLevel.ANONYMOUS` for local development
> convenience. This default will change to `AuthLevel.FUNCTION` in v1.0.
> For production deployments, **always** set `auth_level` explicitly:

```python
import azure.functions as func

from azure_functions_langgraph import LangGraphApp

# Production: require function key authentication
app = LangGraphApp(auth_level=func.AuthLevel.FUNCTION)
```

### Streaming behavior

> **Important:** All `/stream` endpoints (both the native `POST /api/graphs/{name}/stream`
> and the Platform-compatible `POST /threads/{id}/runs/stream` and `POST /runs/stream`)
> return **buffered SSE**. Chunks emitted by the graph are collected during execution
> and flushed as SSE events **after the run completes** — this is **not** true
> token-level streaming, and clients will not receive partial tokens incrementally.
>
> True chunked streaming is on the roadmap and depends on Azure Functions Python v2
> streaming response support. If you need real-time token streaming today, run the
> graph behind a long-running host (e.g. App Service or AKS) instead.

### Per-graph auth

Override app-level auth settings per graph:

```python
# Per-graph authentication override
app.register(graph=public_graph, name="public", auth_level=func.AuthLevel.ANONYMOUS)
app.register(graph=private_graph, name="private", auth_level=func.AuthLevel.FUNCTION)
```

Example request using a function key:

```bash
curl -X POST "https://<app>.azurewebsites.net/api/graphs/echo_agent/invoke?code=<FUNCTION_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"input": {"messages": [{"role": "human", "content": "Hello!"}]}}'
```

### What you get

1. `POST /api/graphs/echo_agent/invoke` — invoke the agent
2. `POST /api/graphs/echo_agent/stream` — stream agent responses (buffered SSE, not true token streaming)
3. `GET /api/graphs/echo_agent/threads/{thread_id}/state` — inspect thread state
4. `GET /api/health` — health check

With `platform_compat=True`, you also get SDK-compatible endpoints:

6. `POST /assistants/search` — list registered assistants
7. `GET /assistants/{id}` — get assistant details
8. `POST /assistants/count` — count assistants
9. `POST /threads` — create thread
10. `GET /threads/{id}` — get thread
11. `PATCH /threads/{id}` — update thread metadata
12. `DELETE /threads/{id}` — delete thread
13. `POST /threads/search` — search threads
14. `POST /threads/count` — count threads
15. `POST /threads/{id}/runs/wait` — run and wait for result
16. `POST /threads/{id}/runs/stream` — run and stream result (buffered SSE)
17. `POST /runs/wait` — threadless run
18. `POST /runs/stream` — threadless stream (buffered SSE)
19. `GET /threads/{id}/state` — get thread state
20. `POST /threads/{id}/state` — update thread state
21. `POST /threads/{id}/history` — get state history

### Request format

```json
{
    "input": {
        "messages": [{"role": "human", "content": "Hello!"}]
    },
    "config": {
        "configurable": {"thread_id": "conversation-1"}
    }
}
```

### Persistent storage (v0.4+)

Use Azure Blob Storage for checkpoint persistence and Azure Table Storage for thread metadata:

```python
import azure.functions as func

from azure.storage.blob import ContainerClient
from langgraph.graph import END, START, StateGraph
from typing_extensions import TypedDict

from azure_functions_langgraph import LangGraphApp
from azure_functions_langgraph.checkpointers.azure_blob import AzureBlobCheckpointSaver
from azure_functions_langgraph.stores.azure_table import AzureTableThreadStore


class AgentState(TypedDict):
    messages: list[dict[str, str]]


def chat(state: AgentState) -> dict:
    user_msg = state["messages"][-1]["content"]
    return {"messages": state["messages"] + [{"role": "assistant", "content": f"Echo: {user_msg}"}]}


# Build graph with Azure Blob checkpointer
container_client = ContainerClient.from_connection_string(
    "DefaultEndpointsProtocol=https;AccountName=...", "checkpoints"
)
saver = AzureBlobCheckpointSaver(container_client=container_client)

builder = StateGraph(AgentState)
builder.add_node("chat", chat)
builder.add_edge(START, "chat")
builder.add_edge("chat", END)
graph = builder.compile(checkpointer=saver)

# Deploy with Azure Table thread store
thread_store = AzureTableThreadStore.from_connection_string(
    "DefaultEndpointsProtocol=https;AccountName=...", table_name="threads"
)

# Production: always set auth_level explicitly
app = LangGraphApp(platform_compat=True, auth_level=func.AuthLevel.FUNCTION)
app.thread_store = thread_store
app.register(graph=graph, name="echo_agent")
func_app = app.function_app
```

Checkpoints and thread metadata survive Azure Functions restarts and scale across instances.

### Scale envelope

The bundled persistent backends are intended for development and small-to-medium production deployments. Plan ahead before pushing past these limits:

| Backend | Comfortable | Caution zone | Switch backends |
|---|---|---|---|
| `AzureBlobCheckpointSaver` | < 100 checkpoints/thread, < 10K threads | 100–1000 checkpoints/thread | Use Cosmos DB or Redis-backed checkpointer |
| `AzureTableThreadStore` | < 100K threads, light search load | 100K–500K threads | Use a sharded thread store or Cosmos DB |

Notes:

- **Single partition** — `AzureTableThreadStore` keys every thread under a single PartitionKey, capped by Azure Table per-partition throughput (~2000 entities/sec on Standard accounts). Search and count beyond `status` filtering are **client-side**; see [COMPATIBILITY.md](COMPATIBILITY.md).
- **Prefix scans** — `AzureBlobCheckpointSaver` lists checkpoints via blob prefix scans; transaction count and latency grow with checkpoints-per-thread. Use the retention helpers below to keep that bounded.
- **Entity size** — Azure Table entities are capped at 1 MB; the store logs a warning at 90% of the threshold.

#### Retention helpers

`AzureBlobCheckpointSaver` exposes two helpers for scheduled cleanup (e.g. from a Timer-triggered Function):

```python
# Keep only the most recent 50 checkpoints per (thread, namespace)
saver.delete_old_checkpoints(thread_id="conversation-1", keep_last=50)

# Or delete everything older than a known checkpoint id
saver.delete_checkpoints_before(
    thread_id="conversation-1",
    before_checkpoint_id="01HXY...",
)
```

Both helpers preserve channel value blobs and the `latest.json` pointer, so retained checkpoints remain fully usable.

### Upgrading

#### v0.3.0 → v0.4.0

Fully backward-compatible. No breaking changes.

- **New optional extras**: `pip install azure-functions-langgraph[azure-blob,azure-table]` for persistent storage
- **New platform endpoints**: thread CRUD, state update/history, threadless runs, assistants count
- **New protocols**: `UpdatableStateGraph`, `StateHistoryGraph` (available from `azure_functions_langgraph.protocols`)

#### v0.4.0 → v0.5.0

Fully backward-compatible. No breaking changes.

- **Metadata API**: `app.get_app_metadata()` returns an immutable snapshot of all registered routes and graph info
- **OpenAPI bridge**: `azure_functions_langgraph.openapi.register_with_openapi` integrates with `azure-functions-openapi-python`
- **CloneableGraph protocol**: thread-isolated graph cloning for safe concurrent execution

## When to use

- You have LangGraph agents and want to deploy them on Azure Functions
- You want serverless deployment without LangGraph Platform costs
- You need HTTP endpoints for your compiled graphs with minimal setup
- You want thread-based conversation state via LangGraph checkpointers
- You need durable state persistence with Azure Blob/Table Storage

## Documentation

- Project docs live under `docs/`
- Smoke-tested examples live under `examples/`
- Product requirements: `PRD.md`
- Design principles: `DESIGN.md`

## Ecosystem

This package is part of the **Azure Functions Python DX Toolkit**.

**Design principle:** `azure-functions-langgraph` owns LangGraph runtime exposure. `azure-functions-validation-python` owns validation. `azure-functions-openapi-python` owns API documentation.

| Package | Role |
|---------|------|
| [azure-functions-openapi-python](https://github.com/yeongseon/azure-functions-openapi-python) | OpenAPI spec generation and Swagger UI |
| [azure-functions-validation-python](https://github.com/yeongseon/azure-functions-validation-python) | Request/response validation and serialization |
| [azure-functions-db-python](https://github.com/yeongseon/azure-functions-db-python) | Database bindings for SQL, PostgreSQL, MySQL, SQLite, and Cosmos DB |
| **azure-functions-langgraph** | LangGraph deployment adapter for Azure Functions |
| [azure-functions-scaffold-python](https://github.com/yeongseon/azure-functions-scaffold-python) | Project scaffolding CLI |
| [azure-functions-logging-python](https://github.com/yeongseon/azure-functions-logging-python) | Structured logging and observability |
| [azure-functions-doctor-python](https://github.com/yeongseon/azure-functions-doctor-python) | Pre-deploy diagnostic CLI |
| [azure-functions-durable-graph-python](https://github.com/yeongseon/azure-functions-durable-graph-python) | Manifest-first graph runtime with Durable Functions *(experimental)* |
| [azure-functions-cookbook-python](https://github.com/yeongseon/azure-functions-cookbook-python) | Recipes and examples |

## For AI Coding Assistants

If you are an AI coding assistant (Copilot, Cursor, Claude, etc.), see:

- [`llms.txt`](llms.txt) — Concise package summary and API overview
- [`llms-full.txt`](llms-full.txt) — Complete API reference with signatures, patterns, and examples

## Disclaimer

This project is an independent community project and is not affiliated with,
endorsed by, or maintained by Microsoft or LangChain.

Azure and Azure Functions are trademarks of Microsoft Corporation.
LangGraph and LangChain are trademarks of LangChain, Inc.

## License

MIT
