API Reference¶
LangGraphApp¶
LangGraphApp(auth_level=func.AuthLevel.FUNCTION, health_auth_level=func.AuthLevel.ANONYMOUS, max_stream_response_bytes=1024 * 1024, max_request_body_bytes=1024 * 1024, max_input_depth=32, max_input_nodes=10000, platform_compat=False, thread_lock=None, route_prefix=_ROUTE_PREFIX, _registrations=dict())
dataclass
¶
Wraps LangGraph compiled graphs into Azure Functions HTTP endpoints.
Usage::
from azure_functions_langgraph import LangGraphApp
app = LangGraphApp()
app.register(graph=compiled_graph, name="my_agent")
func_app = app.function_app
This auto-registers:
POST /api/graphs/{name}/invoke— synchronous invocationPOST /api/graphs/{name}/stream— buffered SSE response (not true streaming)GET /api/health— health check with registered graph listGET /api/graphs/{name}/threads/{thread_id}/state— thread state (StatefulGraph only)
Note
The graph argument must satisfy the :class:LangGraphLike protocol
(i.e. have .invoke() and .stream() methods). This avoids a
hard import dependency on langgraph at the library level.
Note
v0.1 streams are buffered — all chunks are collected and returned in a single SSE-formatted HTTP response. True streaming (chunked transfer encoding) is planned for a future release once Azure Functions Python HTTP streaming stabilises.
Note
The default auth_level is :attr:~azure.functions.AuthLevel.FUNCTION,
so deployed endpoints require a function key by default. Pass
auth_level=func.AuthLevel.ANONYMOUS explicitly for public access
(e.g. local development); doing so emits an unconditional UserWarning.
The health_auth_level parameter controls the auth level of the health
endpoint independently and defaults to :attr:~azure.functions.AuthLevel.ANONYMOUS,
which is the conventional choice for liveness/readiness probes.
Note
Per-thread locking on the native invoke/stream endpoints is
pluggable via :attr:thread_lock. The default,
:class:~azure_functions_langgraph.locks.inprocess.InProcessThreadLock,
is in-process only — it is not distributed across Function App
instances, worker processes, or hosts. For multi-instance production
deployments, supply a distributed backend such as
:class:~azure_functions_langgraph.locks.azure_blob.AzureBlobLeaseThreadLock,
or (for platform-compat runs) enable platform_compat=True with
:class:~azure_functions_langgraph.stores.azure_table.AzureTableThreadStore,
which provides ETag-based atomic locking. Set the
AZFUNC_LANGGRAPH_LOCK_BACKEND environment variable to
distributed to fail-fast at construction if the default
in-process backend is still wired.
function_app
property
¶
Return an azure.functions.FunctionApp with all routes registered.
register(graph, name, description=None, stream=True, auth_level=None, *, request_model=None, response_model=None)
¶
Register a compiled LangGraph graph.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
Any
|
Any object satisfying :class: |
required |
name
|
str
|
Unique name for this graph (used in URL routes). |
required |
description
|
Optional[str]
|
Optional human-readable description. |
None
|
stream
|
bool
|
Whether to enable the stream endpoint for this graph. |
True
|
auth_level
|
Optional[AuthLevel]
|
Override app-level auth for this graph's endpoints.
When |
None
|
request_model
|
Optional[type[Any]]
|
Optional Pydantic model class for request body (used by the metadata / bridge API, not for runtime validation). |
None
|
response_model
|
Optional[type[Any]]
|
Optional Pydantic model class for response body (used by the metadata / bridge API, not for runtime validation). |
None
|
Raises:
| Type | Description |
|---|---|
TypeError
|
If graph does not satisfy the required protocol. |
ValueError
|
If name is already registered or invalid. |
Source code in src/azure_functions_langgraph/app.py
Request models¶
InvokeRequest¶
InvokeRequest
¶
Bases: BaseModel
Request body for graph invocation.
StreamRequest¶
StreamRequest
¶
Bases: BaseModel
Request body for graph streaming.
Response models¶
InvokeResponse¶
InvokeResponse
¶
Bases: BaseModel
Response body for graph invocation.
HealthResponse¶
HealthResponse
¶
Bases: BaseModel
Health check response.
ErrorResponse¶
ErrorResponse
¶
Bases: BaseModel
Error response body.
GraphInfo¶
GraphInfo
¶
Bases: BaseModel
Information about a registered graph.
Protocol interfaces¶
InvocableGraph¶
InvocableGraph
¶
Bases: Protocol
Protocol for a graph that supports synchronous invocation.
StreamableGraph¶
StreamableGraph
¶
Bases: Protocol
Protocol for a graph that supports synchronous streaming.
LangGraphLike¶
LangGraphLike
¶
Bases: InvocableGraph, StreamableGraph, Protocol
Protocol combining invoke and stream — matches LangGraph's CompiledStateGraph.
Thread locks¶
Pluggable per-thread lock backends for the native invoke / stream
endpoints. See Operations & API Surface
for the operational guide (RBAC, lease renewal, production checklist).
ThreadLock¶
ThreadLock
¶
Bases: Protocol
Contract for pluggable per-thread lock backends.
Implementations coordinate concurrent access to a native invoke/stream
request that targets a specific (graph_name, thread_id) so that
single-writer checkpointers (for example
:class:~azure_functions_langgraph.checkpointers.azure_blob.AzureBlobCheckpointSaver)
never see racing writes for one thread.
The two shipped implementations are
:class:~azure_functions_langgraph.locks.inprocess.InProcessThreadLock
(the default; single-process only) and
:class:~azure_functions_langgraph.locks.azure_blob.AzureBlobLeaseThreadLock
(distributed via Azure Blob lease CAS).
Third-party backends satisfying this protocol (Redis, Cosmos DB, etc.)
can be plugged in via :attr:LangGraphApp.thread_lock.
acquire(graph_name, thread_id, timeout=0.0)
¶
Attempt to acquire an exclusive lock for (graph_name, thread_id).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph_name
|
str
|
Registered graph name. |
required |
thread_id
|
str
|
Thread ID drawn from |
required |
timeout
|
float
|
Maximum seconds to wait for the lock. |
0.0
|
Returns:
| Type | Description |
|---|---|
bool
|
|
bool
|
elsewhere (in the same process or, for distributed backends, on |
bool
|
another Function App instance). |
Source code in src/azure_functions_langgraph/locks/base.py
release(graph_name, thread_id)
¶
Release a previously acquired lock.
Must be safe to call even if the lock is not currently held by the
caller — implementations should log at DEBUG level for any
inconsistency rather than raising, so that the handler finally
block never masks the underlying request failure.
Source code in src/azure_functions_langgraph/locks/base.py
InProcessThreadLock¶
InProcessThreadLock()
¶
:class:threading.Lock-based per-thread lock scoped to a single worker.
This is the default backend when :attr:LangGraphApp.thread_lock is not
supplied. It is not distributed — locks are held in this Python
interpreter only and do not coordinate across:
- Multiple Azure Functions App instances (scale-out)
- Multiple worker processes on the same instance
- Warm swaps / cold starts
Multi-instance production deployments must supply a distributed
backend such as
:class:~azure_functions_langgraph.locks.azure_blob.AzureBlobLeaseThreadLock.
Thread-safety
Safe for concurrent acquire / release calls from multiple
threads. Internal state is guarded by a private
:class:threading.Lock.
Source code in src/azure_functions_langgraph/locks/inprocess.py
acquire(graph_name, thread_id, timeout=0.0)
¶
Acquire the lock for (graph_name, thread_id).
See :meth:ThreadLock.acquire for the general contract.
Source code in src/azure_functions_langgraph/locks/inprocess.py
release(graph_name, thread_id)
¶
Release the lock for (graph_name, thread_id).
See :meth:ThreadLock.release for the general contract. Also
garbage-collects the underlying :class:threading.Lock when no
other request currently holds it, so long-lived workers do not
grow the internal dict unboundedly.
Source code in src/azure_functions_langgraph/locks/inprocess.py
AzureBlobLeaseThreadLock¶
AzureBlobLeaseThreadLock(*, container_client, lease_duration=_LEASE_DURATION_MAX, blob_prefix='thread-locks/', auto_renew=True)
¶
Distributed per-thread lock backed by Azure Blob leases.
Coordinates (graph_name, thread_id) locking across multiple Azure
Functions instances by holding an exclusive lease on a marker blob per
thread. Any :class:~azure.storage.blob.ContainerClient will do — the
same container as the AzureBlobCheckpointSaver is a natural fit but
a dedicated container is fine too.
.. warning::
By default this class renews Azure Blob leases in the background
(a per-instance daemon thread renews every active lease at
lease_duration / 3 intervals). Pass auto_renew=False to
opt out — construction then emits a :class:UserWarning because
a finite lease_duration can silently expire mid-execution and
let another instance acquire the same (graph_name, thread_id)
lock, allowing concurrent writes to single-writer checkpointers.
Call :meth:close for graceful shutdown of the renewal thread
when the lock instance is no longer needed; Azure Functions
workers do not typically need this because the daemon thread
dies when the interpreter exits.
Example
from azure.storage.blob import ContainerClient from azure_functions_langgraph import LangGraphApp from azure_functions_langgraph.locks import AzureBlobLeaseThreadLock
container = ContainerClient.from_connection_string(conn, "thread-locks") if not container.exists(): ... container.create_container() lock = AzureBlobLeaseThreadLock(container_client=container) app = LangGraphApp(thread_lock=lock)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
container_client
|
_ContainerClientProtocol
|
An |
required |
lease_duration
|
int
|
Lease length in seconds. Must be 15-60 (finite) or
|
_LEASE_DURATION_MAX
|
blob_prefix
|
str
|
Prefix applied to every marker blob so lock blobs are
visually grouped inside the container. Defaults to
|
'thread-locks/'
|
auto_renew
|
bool
|
If |
True
|
Thread-safety
Safe for concurrent acquire / release calls from multiple
threads. Only one thread in this process can hold a given lease at
a time (the Azure API enforces this globally); this class enforces
it locally by returning False from :meth:acquire when a lease
is already tracked for the key.
Source code in src/azure_functions_langgraph/locks/azure_blob.py
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 | |
acquire(graph_name, thread_id, timeout=0.0)
¶
Attempt to hold an Azure Blob lease for (graph_name, thread_id).
Semantics match :meth:ThreadLock.acquire:
timeout=0.0— non-blocking. Returns immediately.timeout>0.0— polls the Azure API with jittered backoff until the lease is acquired or the deadline expires.
Source code in src/azure_functions_langgraph/locks/azure_blob.py
close()
¶
Stop the renewal thread and release every active lease.
Idempotent and safe to call from any thread. After :meth:close,
further :meth:acquire calls still work but will not be
auto-renewed even if auto_renew=True was passed to the
constructor.
Source code in src/azure_functions_langgraph/locks/azure_blob.py
release(graph_name, thread_id)
¶
Release the Azure Blob lease for (graph_name, thread_id).
Best-effort — never raises. Failures during release are logged at DEBUG and left for lease expiry (or manual break) to recover.
Source code in src/azure_functions_langgraph/locks/azure_blob.py
OpenAPI integration¶
Bridges a LangGraphApp into
azure-functions-openapi-python.
register_with_openapi¶
Registers all graph and app-level routes with the OpenAPI package and returns the
number of routes registered. Requires the optional dependency
azure-functions-openapi-python.
from azure_functions_langgraph import LangGraphApp
from azure_functions_langgraph.openapi import register_with_openapi
app = LangGraphApp()
# ... app.register(...) your graphs ...
count = register_with_openapi(app) # -> int (routes registered)
register_with_openapi(app)
¶
Register all graph and app-level endpoints with azure-functions-openapi-python.
Reads the metadata exposed by :meth:LangGraphApp.get_app_metadata and
calls :func:azure_functions_openapi.register_openapi_metadata for each
route.
.. deprecated:: 0.6.0
Request/response shape now also flows through the shared
_azure_functions_metadata["endpoint"] namespace, which
azure-functions-openapi reads directly from each handler (see
issue #294; umbrella azure-functions-validation#270). This bridge is
retained through the deprecation cycle to keep supplying documentation
metadata (summary/description/tags) that the endpoint
namespace intentionally does not carry.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
app
|
LangGraphApp
|
A :class: |
required |
Returns:
| Type | Description |
|---|---|
int
|
Number of routes registered with the openapi package. |
Raises:
| Type | Description |
|---|---|
ImportError
|
If |
TypeError
|
If a |