LangChain and LangGraph
LangChain tools
Section titled “LangChain tools”from_langchain_tool wraps a LangChain BaseTool as a Pyrula
@tool. Hand the result to an agent like any native tool.
from pyrula.agents.integrations import from_langchain_toolfrom pyrula.agents.decorators.agent import agent
search = from_langchain_tool(my_langchain_tool, timeout=30.0, retry=0)
@agentasync def assistant(ctx, message: str) -> None: async for _ in ctx.llm.stream(messages=[{"role": "user", "content": message}], tools=[search]): ...The tool’s input schema comes from its args_schema when it has one, so the model sees
the same parameters. Tools without a schema fall back to a single input: str. Needs
pip install pyrula-agents[langchain].
LangGraph graphs
Section titled “LangGraph graphs”langgraph_agent wraps a compiled graph as a Pyrula
@agent. The graph runs inside the turn; Pyrula owns the
lifecycle and forwards graph updates into the run stream.
from pyrula.agents.integrations import langgraph_agent
graph_agent = langgraph_agent(my_graph, name="planner", stream_mode="updates")The wrapped agent takes state and an optional config. When stream=True (the
default) it iterates graph.astream(...) and emits each chunk as framework:event,
returning the last chunk as the result. Without streaming it falls back to ainvoke or
invoke. Needs pip install langgraph.
The wrapped agent registers like any other, so create_app
and PyrulaWorker pick it up.
This direction is transport, not durability: the graph re-runs on replay. To get the
other direction, see as_langgraph_node below.
Durable Pyrula nodes in a LangGraph graph
Section titled “Durable Pyrula nodes in a LangGraph graph”as_langgraph_node goes the opposite way: it drops a durable Pyrula
@workflow or @agent into a graph you already run, as one node. That
node’s body is a Pyrula run, so it is journaled, replayable, and crash-safe, and it can
pause for a human.
from pyrula.agents.integrations import as_langgraph_node
node = as_langgraph_node("provision_infra", store=store)graph.add_node("provision", node)The run id is derived from the graph’s thread_id, so resuming the graph re-attaches the
same run rather than starting a new one, and a crash mid-step won’t double-execute. When
the workflow calls ctx.interrupt, the node bridges to LangGraph’s native interrupt() /
Command(resume=...): the graph pauses and checkpoints, and resuming it feeds the
answer back into the Pyrula run. Human-in-the-loop is durable end to end.
It needs a checkpointed graph (for the thread_id and resume) and a Pyrula worker
consuming the store, since the node submits and awaits the run rather than running it
inline. Install with pip install pyrula-agents[langgraph]. For the fuller picture of
which integrations are durable, see
What’s durable, and what isn’t.