Skip to content
Pyrula
Pre-release: the packages are not on PyPI yet, so pip install does not resolve. Build from source to try it today.

Integrations

Pyrula integrations adapt tools, models, and workflows across framework boundaries.

Everything imports from one place:

from pyrula.agents.integrations import (
from_langchain_tool,
durable_chat_model,
as_langgraph_node,
)

Tool adapters wrap a single tool from another framework as a Pyrula @tool. You pass the result to an @agent like any native tool. from_langchain_tool works this way, and the wrapped call is journaled as a durable step inside a run. durable_chat_model gives the same treatment to a LangChain chat model’s own calls.

Durable nodes go the other direction: as_langgraph_node drops a durable Pyrula workflow into a graph you already run, so one graph step is journaled, crash-safe, and resumable, with human-in-the-loop bridged to the host’s own interrupt.

Durability depends on which side owns the journal. See What’s durable, and what isn’t before choosing an integration boundary.

Each integration needs its framework installed. Some have a Pyrula extra:

IntegrationInstall
LangChain toolspip install pyrula-agents[langchain]
LangGraphpip install pyrula-agents[langgraph]

Every durable integration is built on run_and_await: submit a workflow idempotently and wait for its result. Use it to put a durable step inside a framework Pyrula doesn’t ship an adapter for:

from pyrula.workflows import run_and_await
result = await run_and_await(
store, "charge_order", {"order": "o-1"},
idempotency_key="charge:o-1", # becomes the run id; a retry re-attaches
timeout=60,
)

It raises DurableInvocationError if the run fails, DurableInvocationTimeout past timeout, and DurableInvocationCancelled if the run is cancelled. Pass on_interrupt= to answer a ctx.interrupt from the caller’s side; without it the interrupt surfaces as DurableInterrupt. A worker serving the workflow must be running.