Skip to main content

Durable Workflows for AI agents

Make your AI agents resilient to failure and outages

The diagrid package is an extension SDK for the open-source Dapr project to build durable, fault-tolerant AI agents. It integrates seamlessly with popular agent frameworks, wrapping them in Dapr Workflows to ensure agents can recover from failures, persist state across restarts, and scale effectively.

Get started with Diagrid Catalyst for free.

Community

Have questions, hit a bug, or want to share what you're building? Join the Diagrid Community Discord to connect with the team and other users.

Features

  • Multi-Framework Support: Native integrations for LangGraph, CrewAI, Google ADK, Strands, PydanticAI, OpenAI Agents, Claude Agent SDK, LangChain, Smolagents, LangChain Deep Agents, and HolmesGPT.
  • Durability: Agent state is automatically persisted in the database of your choice. If your process crashes, the agent resumes from the last successful step.
  • Fault Tolerance: Built-in retries and error handling powered by Dapr.
  • Observability: Deep insights into agent execution, tool calls, and state transitions.

Installation

Install the base package along with the extension for your chosen framework:

# For LangGraph
pip install "diagrid[langgraph]"

# For CrewAI
pip install "diagrid[crewai]"

# For Google ADK
pip install "diagrid[adk]"

# For Strands
pip install "diagrid[strands]"

# For Pydantic AI
pip install "diagrid[pydantic_ai]"

# For OpenAI Agents
pip install "diagrid[openai_agents]"

# For LangChain Deep Agents
pip install "diagrid[deepagents]"

# For Claude Agent SDK
pip install "diagrid[claude_agents]"

# For LangChain
pip install "diagrid[langchain]"

# For Smolagents
pip install "diagrid[smolagents]"

# For HolmesGPT (install in a dedicated environment — see note below)
pip install "diagrid[holmesgpt]"

Note: diagrid[holmesgpt] is intentionally not part of diagrid[all]. HolmesGPT ships strict pins on fastapi, uvicorn, cachetools, mcp, and httpx[socks] that conflict with the looser constraints used by the other agent extras. Install it in its own environment.

Usage analytics

Every agent runner reports one anonymous usage event when it is constructed, once per package per process. PyPI publishes aggregate download counts only, so this is how Diagrid sees which package versions run on which platforms. Because it is one event per process, a Kubernetes deployment produces one event per replica per restart: the numbers count process starts, not deployments or users.

What is sent: the package name and version, the diagrid-core version, operating system, architecture, Python version, the agent framework and the installed version of its library (framework, framework_version), kind=agent, whether the process points at Catalyst or at a plain Dapr sidecar (target), and whether it runs under a CI variable (ci). Nothing else: no application data, configuration, app IDs, prompts, or hostnames. The receiving service is Scarf. It derives coarse company and location information from the request IP. How Scarf handles that data is described in Scarf's privacy policy.

It never gets in the way: the request runs on a background daemon thread with a one second socket timeout, every failure is swallowed, and the calling thread never waits. Blocked egress and air-gapped clusters behave normally. Nothing is written to your application's output; a DEBUG line on the diagrid.core.analytics logger records whether the event was sent or skipped, for operators who want to confirm an opt-out.

To opt out, set any of these environment variables before starting your application:

export DO_NOT_TRACK=1
# or
export SCARF_NO_ANALYTICS=1
# or
export DIAGRID_NO_ANALYTICS=1

Verified identity

Catalyst signs the calling user's identity into an X-Diagrid-User-Token header on every inbound request. diagrid.identity verifies it before your handler runs, and puts it back on the calls your agent makes on the caller's behalf.

pip install "diagrid[identity]"

Two lines wire it up:

from diagrid.identity import OAuthConfig
from diagrid.identity.asgi import OAuthMiddleware

app.add_middleware(OAuthMiddleware, config=OAuthConfig(scopes={"agent.invoke"}))

Reading the verified caller

from fastapi import Request

from diagrid.identity.asgi import verified_user


@app.post("/invoke")
async def invoke(request: Request):
    user = verified_user(request)  # VerifiedUser | None
    return {
        "subject": user.subject,
        "tenant": user.tenant,
        "scopes": list(user.scopes),
        "admin": user.has_scope("admin.write"),
    }

VerifiedUser carries subject, tenant, scopes, claims and issuer_id, plus has_scope(scope). scopes keeps set semantics but iterates in sorted order, so a response that echoes it is stable across requests and across SDKs.

verified_user() returns None only when the request carried no token and require_auth=False allowed it through. A token that is present is always verified, and an invalid one never reaches the handler.

Outbound calls on behalf of the caller

The sidecar mints the token for the inbound request, so an outbound call has to carry it explicitly. Use the identity-aware client and it rides along:

from diagrid.identity.http import AsyncClient

client = AsyncClient()  # an httpx2.AsyncClient that sends the caller's token

The token is read from the inbound request context at send time, not baked in at construction, so one long-lived client is safe to share: concurrent requests each carry their own caller's token. The header is cleared before it is set, and it only ever travels to the origin the caller addressed — a redirect away from that origin drops it.

For a client you cannot replace, install the same behaviour as a request hook:

import httpx2

from diagrid.identity.http import attach_identity_headers_async

client = httpx2.AsyncClient(event_hooks={"request": [attach_identity_headers_async]})

attach_identity_headers is the synchronous counterpart. A call made with no inbound user context — a cron, pub/sub or scheduled trigger — proceeds unauthenticated with the header omitted rather than raising.

OAuthConfig

Field Type Default Meaning
scopes FrozenSet[str] frozenset() Scopes every caller must carry; a token short of one gets 403.
issuer Optional[str] None Expected iss. Discovered when unset.
audience Optional[str] None Expected aud. Discovered when unset.
jwks_uri Optional[str] None JWKS endpoint. Discovered when unset.
require_auth bool True Reject a request that carries no token. The default is fail-closed, and so is OAuthConfig().
allow_insecure_jwks bool False Opt in to a plaintext JWKS URI on a non-loopback host. The key set is the root of trust, so https is otherwise required; loopback is exempt because that is where the local sidecar serves. It relaxes the rule to plain http only — a file:// JWKS URI, or any other scheme, stays refused.

Discovery precedence

Coordinates come from four sources, in this order:

  1. Explicit config — whatever of issuer, audience and jwks_uri you set on OAuthConfig.
  2. The local sidecar — GET http://127.0.0.1:$DAPR_HTTP_PORT/v1.0/metadata (CATALYST_DAPR_HTTP_PORT wins if both are set). Local is tried before remote so a deployed in-cluster app keeps using the loopback call rather than a network round trip.
  3. The remote sidecar — GET $DAPR_HTTP_ENDPOINT/v1.0/metadata, which is the diagrid dev run shape: the app runs on your machine against a Catalyst-hosted sidecar, so nothing is listening on 127.0.0.1. DAPR_API_TOKEN is sent as the dapr-api-token header when it is set.
  4. Environment variables — DIAGRID_DP_SENTRY_ISSUER and DIAGRID_DP_SENTRY_AUDIENCE.

jwks_uri resolves explicit first, then the value the sidecar advertises — adopted only when the discovered issuer is the issuer being verified, so a pinned issuer is never checked against a foreign issuer's keys — and otherwise issuer + /jwks.json. If nothing resolves, a token-carrying request is answered 503 oauth.not_configured rather than let through.

Tokens are accepted for RS256 and ES256 only, must carry exp, iss and sub, are allowed 120s of clock skew, and the key set is cached for 300s.

Supplying the verifier yourself

OAuthMiddleware builds its own verifier from the coordinates above. Pass one instead — a pre-built JWKSVerifier, or any object satisfying the TokenVerifier protocol — when the app resolves coordinates its own way, or to stand a double in during a test:

app.add_middleware(OAuthMiddleware, config=OAuthConfig(), verifier=my_verifier)

The seam is on the middleware, not on OAuthConfig, which stays pure policy.

Rejections

Every rejection is {"error": "<code>"} with Cache-Control: no-store. The codes are constants on OAuthErrorCodes.

Status Code When
401 oauth.missing_token No X-Diagrid-User-Token, and require_auth=True.
401 oauth.expired exp is in the past, beyond the skew allowance.
401 oauth.invalid_issuer iss is not the expected issuer.
401 oauth.invalid_audience aud is not the expected audience.
401 oauth.invalid_signature Signature does not verify against the key set.
401 oauth.decode_error The token is malformed.
401 oauth.invalid_token Any other claim validation failure.
403 oauth.missing_scope Verified, but short of OAuthConfig.scopes.
503 oauth.not_configured No identity coordinates could be resolved.
503 oauth.verifier_unavailable Key material has not loaded yet.

Prerequisites

  • Python: 3.11 or higher

Examples & Quickstarts

Two paths to your first running agent:

Getting Started with Diagrid Catalyst

Diagrid Catalyst is a fully managed workflow engine for AI agents, built on the open-source CNCF Dapr Workflow project. It's the easiest way to test the different agentic integrations for free.

See quickstarts to get started in less than 5 minutes.

How It Works

This SDK leverages Dapr Workflows to orchestrate agent execution.

  1. Orchestration: The agent's control loop is modeled as a workflow.
  2. Activities: Each tool execution or LLM call is modeled as a durable activity.
  3. State Store: Dapr saves the workflow state to a configured state store (e.g., Redis, CosmosDB) after every step.

Your code can run anywhere (local machine, Kubernetes, EC2, etc.) while the fully managed workflow engine takes care of the agent's execution state, making it crash-proof and resilient to any outage or failure.

Metadata

Release files for diagrid 0.5.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for diagrid 0.5.3
File Size Uploaded
diagrid-0.5.3.tar.gz 187.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for diagrid 0.5.3
File Interpreter ABI Platform
diagrid-0.5.3-py3-none-any.whl Python 3 none any Details

Total release size: 429.8 kB

Release files / diagrid-0.5.3.tar.gz

Download URL diagrid-0.5.3.tar.gz
Size 187.2 kB
Tags Source
SHA-256 checksum
How to use checksums
e556ca781ff0e859c89238a21c0a94335ec1f85baeba5dbdde09bae41716d50a
BLAKE2b-256 checksum
How to use checksums
e26beb5932ddfdef94809af5861e04b33a41c1632164bbfa454150e47d5068a3
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 Oct 2, 2026.

Transparency log

Release files / diagrid-0.5.3-py3-none-any.whl

Download URL diagrid-0.5.3-py3-none-any.whl
Size 242.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a2a90b4a6d4900c83bea987ec44e2eb8733f8b0cdcf9831bd721bd752123b6fe
BLAKE2b-256 checksum
How to use checksums
e9dadfc00253c347619098e465eca65306edc048730e5c3d992e9252703bdfec
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 Oct 2, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.5.3 This release

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.7

2 release files

0.4.6

2 release files

0.4.5

2 release files

0.4.4

2 release files

0.4.3

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page