Integrating KirokuForms with LangGraph

Leverage KirokuForms for seamless human-in-the-loop (HITL) workflows within your LangGraph applications. This guide covers the best practices for pausing your graphs to wait for human input.

How it Works: The HITL Flow

Integrating human feedback into an AI agent's workflow is critical for tasks requiring verification, correction, or creative input. The KirokuForms-LangGraph integration allows you to seamlessly pause a graph's execution, request input from a human via a web form, and then resume the graph with the collected data.

graph TD;A[Start]-->B(Agent Processes Data);B-->C{Needs Human Review?};C--No-->F(Continue Workflow);C--Yes-->D(Pause Graph & Create KirokuForm Task);D-->E(Human Completes Form);E-->F;F-->G[End];style D fill:#fef3c7,stroke:#f59e0b,stroke-width:2px;style E fill:#dbeafe,stroke:#3b82f6,stroke-width:2px;

A typical Human-in-the-Loop workflow within LangGraph.

Installation & Setup

First, install the necessary packages and have your KirokuForms API key ready.

Install Packages
pip install langgraph git+https://github.com/ChelseaAIVentures/langgraph-kirokuforms.git

Get Your API Key

You'll need an API key from the KirokuForms Developer Dashboard to authenticate with the service. Keep it secure, for example, by loading it from an environment variable.

Choosing Your Integration Method

There are two primary methods for integrating KirokuForms HITL into LangGraph. The best choice depends on your application's complexity and your preference for control versus convenience.

Method Best For How it Works
Interrupt Handler (Recommended) Most use cases. It's the most integrated and idiomatic LangGraph approach. You configure the graph to pause automatically before a specific node. A KirokuForms handler creates the HITL task for you.
Direct Node Integration Complex workflows where you need fine-grained, conditional control over when a review is requested. You explicitly call the KirokuForms client from within a graph node to create tasks and fetch results.

Recommended: Using LangGraph Interrupts

LangGraph's interrupt capability is the cleanest way to handle HITL. You configure your graph to pause before a specific node runs, and our handler automatically creates the KirokuForm task.

What is Checkpointing?

For a graph to pause and resume, it needs to save its state. This is called checkpointing. LangGraph provides various "savers" (e.g., `MemorySaver`, `SQLiteSaver`) for this purpose. Using interrupts requires a checkpointer.

Step 1: Create the Interrupt Handler

The `create_kiroku_interrupt_handler` function from our SDK creates a ready-to-use handler for LangGraph.

kiroku_handler.py
from kirokuforms import create_kiroku_interrupt_handler

# This factory creates a handler that will be called by LangGraph.
# It automatically creates a KirokuForms task when the interrupt is triggered.
interrupt_handler = create_kiroku_interrupt_handler(
    api_key="YOUR_KIROKU_API_KEY"
)

Step 2: Compile the Graph with the Interrupt

When compiling your graph, tell it to `interrupt_before` a specific node and provide a checkpointer.

graph_setup.py
from langgraph.graph import StateGraph, END
from langgraph.checkpoint import MemorySaver

# Define a simple graph
workflow_builder = StateGraph(State)
workflow_builder.add_node("agent", agent_node) # Your main agent logic
workflow_builder.set_entry_point("agent")
workflow_builder.add_edge("agent", END)

# Compile the graph with checkpointing and the interrupt
# The graph will pause BEFORE the "agent" node executes.
memory = MemorySaver()
app = workflow_builder.compile(
    checkpointer=memory,
    interrupt_before=["agent"] 
)

Step 3: Invoke and Resume

When you invoke the graph, it will now automatically pause. After the human provides input, you invoke it again with `None` to resume from where it left off.

main.py
# This config is essential for checkpointing to work
thread_config = {"configurable": {"thread_id": "user-123-thread-1"}}

# Invoke the graph. It will run up to the interrupt and pause.
# The KirokuForms interrupt_handler is called automatically.
app.invoke({"messages": ["Initial user prompt"]}, thread_config)

# At this point, a HITL task is active in KirokuForms.
# After the human submits the form, you can resume the graph.
# Passing None for the input tells LangGraph to continue from the saved state.
final_result = app.invoke(None, thread_config)
print(final_result)

Alternative: Direct Node Integration

For more control, you can call the `KirokuFormsHITL` client directly inside your graph nodes. This approach requires you to manage the state and the polling/resumption logic more explicitly.

hitl_nodes.py
from kirokuforms import KirokuFormsHITL

kiroku_client = KirokuFormsHITL(api_key="YOUR_KIROKU_API_KEY")

# 1. Define the node that creates the HITL task
def request_verification_node(state: State):
    data_to_verify = state['data_to_verify']
    task = kiroku_client.create_verification_task(
        title="Verify Transaction",
        data=data_to_verify
    )
    print(f"Human review requested. Task ID: {task['taskId']}")
    print(f"Please complete the form at: {task['reviewUrl']}")
    return {"task_id": task['taskId'], "status": "PENDING_REVIEW"}

# 2. Define the node that processes the result
def process_feedback_node(state: State):
    task_id = state['task_id']
    
    # In a real app, you would poll or use a webhook.
    # Here, we poll for the result.
    print("Polling for task result...")
    result = kiroku_client.get_task_result(task_id, wait=True, timeout=300)
    
    print("Review complete!")
    return {"human_response": result, "status": "REVIEW_COMPLETE"}

In this pattern, you would typically use conditional edges in your graph to route to the `request_verification_node`. After the task is created, the workflow would end. A separate process (e.g., a webhook trigger or a scheduled job) would then re-invoke the graph to run the `process_feedback_node`.

Advanced Configuration

Building Custom Forms

You can define a rich set of form fields to capture precisely the information you need from humans, including text inputs, selections, file uploads, and more.

Defining Custom Form Fields
task_fields = [
    {"type": "text", "label": "Company Name", "name": "company", "required": True},
    {"type": "number", "label": "Revenue (USD)", "name": "revenue"},
    {"type": "textarea", "label": "Additional Notes", "name": "notes"},
    {
        "type": "select", 
        "label": "Industry", 
        "name": "industry",
        "options": [
            {"label": "Technology", "value": "tech"},
            {"label": "Healthcare", "value": "health"},
        ]
    },
    {
        "type": "radio", 
        "label": "Verification Status", 
        "name": "status",
        "required": True,
        "options": [
            {"label": "Verified", "value": "verified"},
            {"label": "Rejected", "value": "rejected"}
        ]
    },
    {"type": "file", "label": "Upload Supporting Document", "name": "support_doc"}
]

# Pass these fields when creating a task
# kiroku_client.create_task(title="Data Entry", fields=task_fields, ...)

Asynchronous Resumption with Webhooks

Instead of polling for results, you can provide a `webhook_url` when creating a task. KirokuForms will send a POST request to your URL when the task is complete, allowing you to resume the correct LangGraph workflow immediately. See our Webhook Security Guide for more details.

Next Steps

Now that you've seen how to integrate KirokuForms with LangGraph, explore these resources: