Skip to main content

Human-in-the-loop for LangGraph and LangChain: a blocking ask_human, plus a durable interrupt()/Command resume that reaches your user on their phone.

Project description

pushary-langgraph

Human-in-the-loop for LangGraph and LangChain. Ask a real human to approve, and get the answer on their phone. Two seams:

  • A blocking ask_human you call from inside a node.
  • A durable pushary_interrupt that parks the graph with LangGraph's native interrupt() and resumes on a signed webhook, so a long wait holds no compute and survives a restart.

Requires the Pushary Partner plan.

Install

pip install pushary-langgraph

Set PUSHARY_API_KEY (get it in your dashboard).

Connect a phone once

from pushary_langgraph import connect

link = connect("user_123")  # show this to your end-user; one tap connects their phone

Ask a human inside a node

from pushary_langgraph import ask_human

def approval_node(state):
    d = ask_human("Approve this transfer?", external_id=state["user_id"], node="approval")
    return {"approved": d["approved"]}

ask_human blocks, polls durably, and fails closed. The idempotency key is derived from external_id + node + question, so a node that re-runs on resume hits the same decision instead of paging the human twice.

Durable interrupt

from pushary_langgraph import pushary_interrupt

def approval_node(state):
    answer = pushary_interrupt(
        "Approve this transfer?",
        external_id=state["user_id"],
        node="approval",
        callback_url=os.environ["PUSHARY_CALLBACK_URL"],  # omit to block instead of park
    )
    return {"approved": answer == "yes"}

With a callback_url, the node opens the decision and calls LangGraph's interrupt() to park the graph (a checkpointer is required). Keep any code before the call idempotent, the whole node re-runs on resume.

Resume from the webhook

from pushary_langgraph import resolve_pushary_callback, SIGNATURE_HEADER
from langgraph.types import Command

# POST /pushary/callback
def callback(request):
    raw = request.body
    cb = resolve_pushary_callback(raw, request.headers.get(SIGNATURE_HEADER), os.environ["PUSHARY_WEBHOOK_SECRET"])
    if not cb:
        return ("bad signature", 401)
    thread_id = lookup_thread(cb["correlationId"])  # your own correlationId -> thread_id map
    graph.invoke(Command(resume=cb["answer"]), {"configurable": {"thread_id": thread_id}})
    return ("ok", 200)

API

  • connect(external_id, *, api_key=None, base_url=None) — enroll an end-user's phone, returns the link.
  • ask_human(question, *, external_id, type="confirm", options=None, node=..., ...) — blocking, returns the decision dict.
  • pushary_interrupt(question, *, external_id, node=..., callback_url=None, ...) — blocking, or durable when callback_url is set.
  • resolve_pushary_callback(raw_body, signature, secret) — verify + parse a callback into {correlationId, answer, approved, ...}.
  • describe_answer(type, result), is_affirmative(answer), deterministic_key(parts), SIGNATURE_HEADER.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pushary_langgraph-0.1.0.tar.gz (7.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pushary_langgraph-0.1.0-py3-none-any.whl (6.0 kB view details)

Uploaded Python 3

File details

Details for the file pushary_langgraph-0.1.0.tar.gz.

File metadata

  • Download URL: pushary_langgraph-0.1.0.tar.gz
  • Upload date:
  • Size: 7.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for pushary_langgraph-0.1.0.tar.gz
Algorithm Hash digest
SHA256 e6ae8de8cbd4993b38bd5429966e6bd52165d4abb455cf01319d00fb06116d60
MD5 0abce0969e44ae7a716a87388cd32d40
BLAKE2b-256 b5a59d5d517ef7132d5c3715a6c1c54f77fa74b67f3e8e5e108a7577c8883545

See more details on using hashes here.

File details

Details for the file pushary_langgraph-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for pushary_langgraph-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6443083555eafa132b572038529a43a9fc80e2538d209a91d2409abdc6916f19
MD5 a51cf22b51b0640a733c1ead37e5a8fb
BLAKE2b-256 69c0983169f5ddcb783fb81e0b4dc540aae92d0667325a8bcdfaf59de22be9a4

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page