Skip to main content
This example is a support agent built with LangChain’s create_agent. It looks orders up with a tool, and its refund tool asks a person with LangGraph’s interrupt(): create_agent builds a LangGraph graph, so the SDK’s LangGraph adapter turns the interrupt into an AgentOS approval and continues the graph with the decision.
Tested with langchain 1.4.7, langchain-openai 1.7.3 and LangGraph 1.2.18 on Python 3.13, using the conformance kit: 25 checks passed, including an approved, an edited and a rejected refund.

The bundle

The SDK wheel goes in the bundle’s vendor/ folder, installed with its langgraph extra; see SDKs.

How the model is wired

ChatOpenAI gets the gateway’s base URL, the run token in default_headers (x-agentos-run-token) and inv.llm.httpx_client(), an HTTP client that never follows redirects and never sends the token to another host. The API key is ignored by the gateway. The model name from config.models reaches the gateway exactly as written (anthropic/claude-sonnet-4-5). The model and the agent are built inside the handler, on every invocation, because the token belongs to one invocation. With max_retries=0 a guardrail block isn’t retried; agentos.guardrail_block(error) finds it inside LangChain’s error, and the run fails with blocked_by_guardrail and the guardrail’s message. See Guardrails.

How the approval works

1

The tool interrupts

issue_refund calls interrupt() with an ask: an intent, one item (the order and amount, which a reviewer can edit), the actions and details. The agent is compiled with langgraph.checkpointer(), and langgraph.run() keeps the graph’s checkpoint in the run’s state and asks the hold. The invocation answers suspended.
2

A person decides

The run is held until someone decides in the Review queue or through the Holds API.
3

The graph continues

On the resume langgraph.run() restores the checkpoint and continues the graph. The tool runs again from its start, and this time interrupt() returns the decision: items with each item’s action, data (as approved or edited) and comment. An empty items means the ask expired.
Everything in the tool before interrupt() runs again on the resume. Keep it free of side effects: look things up, but pay, send or write only after the decision.

Run it

1

Zip and deploy

Put the SDK wheel in vendor/ (see SDKs), set config.models.main to a model from your Models page, then zip the folder’s contents and upload them:
2

Ask a question

3

Ask for a refund

The run is held: the refund waits in the Review queue. A reviewer approves it, edits the amount, or rejects it there, or through the Holds API. The item’s id is the order id.

Test it before you deploy

The conformance kit builds the bundle and runs it against a mock platform whose model follows call:<tool> {arguments} in a message, so these cases drive the tools and every decision without a real model:
conformance.yaml

Things to know

  • The checkpoint travels in run state. A run’s state value can be at most 1 MiB, so a very long conversation needs trimming.
  • Build the agent per invocation. Building the model or the agent at module level would keep the first invocation’s token, which stops working when that invocation answers.
  • More about the adapter, including several graphs in one handler: Frameworks.

Next steps

Human approvals

Everything an ask can carry: several items, details, files, an expiry.

More frameworks

The same agent in other frameworks and languages.