KirokuForms Python SDK

Seamlessly integrate KirokuForms' Human-in-the-Loop (HITL) capabilities into your Python applications and LangGraph agents. Our SDK simplifies interaction with our Model Context Protocol (MCP) compliant server, making it easy to create, manage, and retrieve results from human review tasks.

Overview

The langgraph-kirokuforms Python library provides a high-level client specifically designed for developers working with KirokuForms' Human-in-the-Loop (HITL) features. Its primary goals are to:

  • Enable programmatic creation and management of HITL tasks.
  • Allow dynamic definition of review forms using Python dictionaries or leverage pre-configured KirokuForms templates.
  • Offer native integration with LangGraph through a dedicated interrupt handler, simplifying the addition of human review checkpoints in complex agentic workflows.
  • Facilitate the retrieval of structured human feedback to guide and improve your AI systems.
  • Ensure interactions are compliant with the Model Context Protocol (MCP) for standardized and interoperable AI-human collaboration.

Installation

As the KirokuForms Python SDK (langgraph-kirokuforms) is not yet published on PyPI, you can install it directly from its GitHub repository using pip:

Installation
# Install from GitHub
pip install git+https://github.com/ChelseaAIVentures/langgraph-kirokuforms

# For development (after cloning the repository):
# pip install -e ".[dev]"

Please ensure you have Git installed on your system to use this installation method. If you plan to contribute to the SDK or need the latest unreleased changes, clone the repository and install it in editable mode.

Configuration & Initialization

To begin using the SDK, you will need your KirokuForms API Key. The SDK defaults to using the production KirokuForms MCP API endpoint.

API Key & Base URL

You can obtain your API Key from the Developer Settings page in your KirokuForms dashboard. Ensure the key has the appropriate scopes for HITL operations (e.g., hitl:tasks:create, hitl:tasks:read). For security, store your API key in an environment variable (e.g., KIROKU_API_KEY) rather than hardcoding it in your application.

The default base_url for the SDK is https://www.kirokuforms.com/api/mcp. Adjust this if you are using a different KirokuForms environment.

SDK Initialization
from kirokuforms import KirokuFormsHITL, create_kiroku_interrupt_handler

# Initialize the KirokuForms client directly
kiroku_hitl_client = KirokuFormsHITL(
    api_key="YOUR_KIROKU_API_KEY",
    base_url="https://www.kirokuforms.com/api/mcp"  # Default, adjust if needed
    # Optional: webhook_url, webhook_secret, timeout, max_retries
)

# Or, create a LangGraph interrupt handler (which internally creates a client)
human_review_interrupt = create_kiroku_interrupt_handler(
    api_key="YOUR_KIROKU_API_KEY",
    base_url="https://www.kirokuforms.com/api/mcp"  # Default
)

The KirokuFormsHITL client constructor also accepts optional parameters like webhook_url and webhook_secret (for global webhook notifications on task events, if your KirokuForms setup supports this globally beyond per-task callback_url), timeout (for API requests), and max_retries.

Core Concepts

Model Context Protocol (MCP)

KirokuForms' HITL capabilities are built upon the Model Context Protocol. The SDK abstracts the direct MCP interactions, providing Pythonic methods to request human input, define review interfaces, and retrieve structured feedback, all while ensuring MCP compliance. Learn more about MCP at KirokuForms.

LangGraph Interrupts & Checkpoints

The create_kiroku_interrupt_handler is specifically designed for LangGraph. When an agent in your graph needs human input, this handler can be invoked. It will:

  1. Pause (interrupt) the graph's execution.
  2. Create a KirokuForms HITL task using the provided configuration (title, form fields, etc.).
  3. If wait_for_completion is true (default), it blocks until the human reviewer submits the form.
  4. The human's input is then fetched and injected back into the LangGraph state, typically under a key like human_verification or as per your state definition.
  5. The graph execution can then resume, branching based on the human feedback.
This process leverages LangGraph's checkpointing system to persist and restore the graph's state across the human review step.

SDK API Reference

Class: KirokuFormsHITL

The main client class for all direct interactions with the KirokuForms HITL API.

Constructor

KirokuFormsHITL Constructor
KirokuFormsHITL(
    api_key: str,
    base_url: str = "https://www.kirokuforms.com/api/mcp",
    webhook_url: Optional[str] = None,
    webhook_secret: Optional[str] = None,
    timeout: int = 10,
    max_retries: int = 3
)
  • api_key: str: Your KirokuForms API key.
  • base_url: str: API base URL (defaults to KirokuForms production MCP).
  • webhook_url: Optional[str]: Global webhook URL for task event notifications (if distinct from per-task callback_url).
  • webhook_secret: Optional[str]: Secret for the global webhook.
  • timeout: int: HTTP request timeout in seconds (default: 10).
  • max_retries: int: Max retries for failed requests (default: 3).

Method: create_task

create_task Parameters
create_task(
    title: str,
    description: str = "",
    fields: Optional[List[Dict[str, Any]]] = None,
    template_id: Optional[str] = None,
    initial_data: Optional[Dict[str, Any]] = None,
    expiration: Optional[str] = None,  # e.g., "2h", "1d"
    priority: str = "medium",          # "low", "medium", "high"
    task_id: Optional[str] = None,     # Client-defined external ID for idempotency
    callback_url: Optional[str] = None # URL for async notification on task completion/update
) -> Dict[str, Any]

Creates a new HITL task. You must provide either fields for a dynamically defined form or a template_id to use a pre-existing KirokuForms form template.

Returns: A dictionary with task details: taskId (KirokuForms ID for the task), formId (ID of the form used/created), and formUrl (direct URL for the human reviewer).

Example: Creating a dynamic task
Dynamic Task Creation Example
# Example: Using KirokuFormsHITL client to create a task with dynamic fields
task_details = kiroku_hitl_client.create_task(
    title="Verify Customer Details",
    description="Please review and confirm the customer information below.",
    fields=[
        {
            "type": "text", "label": "Customer Name", "name": "customer_name",
            "required": True, "defaultValue": "Acme Corp"
        },
        {
            "type": "email", "label": "Contact Email", "name": "contact_email",
            "required": True, "defaultValue": "contact@acme.corp"
        },
        {
            "type": "radio", "label": "Is Information Valid?", "name": "is_valid",
            "required": True, "options": [
                {"label": "Yes, Valid", "value": "yes"},
                {"label": "No, Invalid", "value": "no"}
            ]
        }
    ],
    initial_data={"customer_id": "CUST12345"},  # Optional context data
    priority="high"
)

print(f"Task created! Review URL: {task_details.get('formUrl')}")
print(f"Task ID: {task_details.get('taskId')}")

# To get results (blocking example):
# try:
#   submission_data = kiroku_hitl_client.get_task_result(task_details.get('taskId'), wait=True, timeout=600)
#   print("Submission received:", submission_data)
# except TimeoutError:
#   print("Task was not completed in time.")

Method: create_verification_task

create_verification_task Parameters
create_verification_task(
    title: str,
    description: str,
    data: Dict[str, Any],  # Data to be verified; SDK auto-generates fields
    fields: Optional[List[Dict[str, Any]]] = None,  # Optional custom fields to append
    **kwargs: Any  # Other params for create_task (e.g., priority, expiration)
) -> Dict[str, Any]

A convenience method to create a HITL task specifically for verifying a dictionary of data. The SDK attempts to generate appropriate form fields based on the data types. You can also append custom fields.

Returns: Same as create_task.

Method: get_task_result

get_task_result Parameters
get_task_result(
    task_id: str, 
    wait: bool = True, 
    timeout: int = 3600  # Seconds; only applies if wait=True
) -> Dict[str, Any]

Retrieves the result (submitted form data) of a completed HITL task. If wait=True (default), this method will poll the API and block until the task is completed or the timeout is reached. If wait=False, it returns the current status immediately; if the task isn't completed, it will raise an error or indicate pending status (check SDK's specific behavior for non-blocking calls, which might involve checking the status in the returned dictionary).

Returns: A dictionary containing the submitted form data once the task is completed. Raises TimeoutError if wait=True and task is not completed within timeout.

Method: list_tasks

(Coming soon: Detailed parameters and example from README) - Lists HITL tasks, with optional filters for status, limit, and offset.

Method: cancel_task

(Coming soon: Detailed parameters and example from README) - Cancels a pending HITL task.


Utility: create_kiroku_interrupt_handler

Factory function specifically for LangGraph integration.

create_kiroku_interrupt_handler
create_kiroku_interrupt_handler(
    api_key: str, 
    **kwargs: Any  # Passed to KirokuFormsHITL constructor (e.g., base_url)
) -> Callable[[Dict[str, Any], Dict[str, Any]], Dict[str, Any]]

# The returned handler expects:
# handler(state: Dict[str, Any], interrupt_data: Dict[str, Any])
# interrupt_data keys: title, description, fields/data, wait_for_completion

Creates a callable LangGraph node that initiates a KirokuForms HITL task. The interrupt_data dictionary passed to the handler configures the task (title, description, fields/data for the form, and wait_for_completion flag).

The handler modifies the LangGraph state, typically adding a human_verification key containing task_id, form_url, and the result (submitted data from the form) upon completion if wait_for_completion is true.

Example: Full LangGraph Integration
Complete LangGraph Integration Example
from langgraph.graph import StateGraph, END
from typing import TypedDict, Annotated, List
import operator

# Define your agent state if using typing
class AgentState(TypedDict):
    input_query: str
    llm_response: str
    needs_human_review: bool
    # Store human feedback, e.g., as a list of dictionaries
    human_review_feedback: Annotated[List[dict], operator.add] 

# Initialize KirokuForms interrupt handler
human_review_interrupt = create_kiroku_interrupt_handler(api_key="YOUR_KIROKU_API_KEY")

def llm_agent_node(state: AgentState):
    # Your agent's logic
    response = f"AI response to: {state['input_query']}"  # Replace with actual LLM call
    # Determine if human review is needed based on your criteria
    needs_review = True  # Example: always review for now
    return {"llm_response": response, "needs_human_review": needs_review}

def human_review_node(state: AgentState):
    if state.get("needs_human_review"):
        print("Requesting human review for the AI response...")
        interrupt_config = {
            "title": "Review AI Generated Response",
            "description": f"Query: '{state['input_query']}'. Please review the AI's response and provide feedback.",
            "fields": [
                {"type": "textarea", "label": "AI Response (edit if needed)", "name": "ai_edited_response", "defaultValue": state["llm_response"], "required": True, "rows": 5},
                {"type": "radio", "label": "Approve this response?", "name": "approved_status", "options": [{"label":"Yes, Approve", "value":"yes"}, {"label":"No, Needs Revision", "value":"no"}], "required": True},
                {"type": "textarea", "label": "Reasoning or Correction Notes", "name": "reviewer_notes", "required": False, "rows": 3}
            ],
            "wait_for_completion": True  # True = block until human input, False = proceed and check later/webhook
        }
        # The interrupt_handler pauses the graph, creates a KirokuForms task,
        # and returns the state updated with 'human_verification' containing the result.
        updated_state_after_review = human_review_interrupt(state, interrupt_config)
        
        # Optionally, process and store the review result more directly in your state
        if updated_state_after_review.get("human_verification", {}).get("completed"):
            review_data = updated_state_after_review["human_verification"]["result"]
            # Example: appending feedback to a list in the state
            current_feedback = state.get("human_review_feedback", [])
            return {**updated_state_after_review, "human_review_feedback": current_feedback + [review_data], "needs_human_review": False}
        return updated_state_after_review
        
    return state  # Should only happen if needs_human_review was false

def route_after_review(state: AgentState):
    last_review = state.get("human_review_feedback", [{}])[-1]
    if last_review.get("approved_status") == "yes":
        print("Human approved. Ending workflow.")
        return END
    else:
        print("Human requested revision. Potentially loop back or send to another handler.")
        # Example: Could loop back to llm_agent_node or a specific rework_node
        return "llm_agent_node"  # Or another state

# Build the graph
workflow = StateGraph(AgentState)
workflow.add_node("llm_agent_node", llm_agent_node)
workflow.add_node("human_review_node", human_review_node)

workflow.set_entry_point("llm_agent_node")

workflow.add_conditional_edges(
    "llm_agent_node",
    lambda state: "human_review_node" if state.get("needs_human_review") else END,
)
workflow.add_conditional_edges(  # New conditional edge from human_review_node
    "human_review_node",
    route_after_review
)

app = workflow.compile()

# Example invocation:
# initial_state = {"input_query": "Explain quantum computing simply.", "human_review_feedback": []}
# for event in app.stream(initial_state, {"recursion_limit": 10}):
#   print("\n--- Event ---")
#   for key, value in event.items():
#       print(f"{key}: {value}")
# final_state = event.get(list(event.keys())[-1])  # Get the state from the last event part

Defining Form Fields for HITL Tasks

When you use create_task or the create_kiroku_interrupt_handler with dynamic fields, you provide a list of Python dictionaries. Each dictionary defines a field. Here are commonly supported types and their properties (refer to the KirokuForms form builder and main API documentation for a complete list of all supported field types and advanced validation options):

| Type | Description | Key Properties (from SDK/OpenAPI FormField) | |------|-------------|---------------------------------------------| | text | Single-line text input | label, name, required, defaultValue, placeholder, validation | | textarea | Multi-line text input | label, name, required, defaultValue, placeholder, rows, validation | | number | Numeric input | label, name, required, defaultValue, min, max, step, validation | | email | Email input with validation | label, name, required, defaultValue, placeholder, validation | | select | Dropdown selection | label, name, required, defaultValue, options (array of {label, value}), validation | | radio | Radio button group | label, name, required, defaultValue, options (array of {label, value}), validation | | checkbox | Checkbox or checkbox group | label, name, required, defaultValue, options (array of {label, value} for groups), validation | | date | Date picker | label, name, required, defaultValue, min, max, validation | *Note: See KirokuForms form builder or API reference for all supported field types and properties, like tel, url, password, file, etc. The validation object can hold rules like minLength, maxLength, pattern.*

Troubleshooting SDK Issues

If you encounter problems while using the Python SDK:

  • Authentication Errors (e.g., 401 or 403 status codes):
    • Verify your api_key is correct, active, and has the necessary permissions (scopes) for HITL operations in your KirokuForms dashboard.
    • Ensure the base_url correctly points to the KirokuForms MCP API endpoint.
  • Task Creation Failures (e.g., 400 or 422 status codes):
    • Check the structure of your fields definitions against the Field Types table and ensure all required properties for each field type are included.
    • If using template_id, confirm the template exists and is accessible.
    • Ensure the title for the task is provided.
    • Look for detailed error messages in the API response, often in result.get("error", ).get("message") or result.get("error", ).get("details").
  • get_task_result Timeouts or Errors:
    • If using wait=True, the human reviewer must complete the task within the specified timeout period.
    • For asynchronous workflows (wait=False or if using callback_url), ensure your system correctly handles the callback or polls appropriately. The SDK's get_task_result with wait=False might indicate a pending status if called too early; check the specific behavior documented by the SDK for non-blocking calls.
  • LangGraph Integration Problems:
    • Ensure your LangGraph state object (e.g., AgentState) is correctly defined to receive and store the output from the human_review_interrupt handler (typically under a key like human_verification or human_input).
    • Verify that the interrupt_data dictionary passed to the handler correctly defines the HITL task (title, fields/data).
  • Enable SDK Logging:

    The SDK uses Python's standard logging module with the logger name "kirokuforms". To see detailed debug messages from the SDK, configure logging in your application:

    Enable SDK Logging
    import logging
    
    # For verbose output from the SDK and other libraries:
    logging.basicConfig(level=logging.DEBUG) 
    
    # For less verbose output, focusing on info and warnings:
    # logging.getLogger("kirokuforms").setLevel(logging.INFO) 
    # logging.getLogger().setLevel(logging.INFO)  # Set root logger level

For further assistance with HITL concepts or API behavior, refer to the main HITL API Guide or contact our support team.

SDK Development & Contributions

The langgraph-kirokuforms Python SDK is an open-source project. We welcome community contributions, bug reports, and feature requests.

  • GitHub Repository: https://github.com/ChelseaAIVentures/langgraph-kirokuforms
  • Setting up for Development:
    Development Setup
    # Clone the repository
    git clone https://github.com/ChelseaAIVentures/langgraph-kirokuforms
    
    # Navigate into the directory
    cd langgraph-kirokuforms
    
    # Create and activate a Python virtual environment (recommended)
    python -m venv venv
    source venv/bin/activate  # On Windows: venv\Scripts\activate
    
    # Install in editable mode with development dependencies
    pip install -e ".[dev]"
  • Running Tests:

    The SDK includes a test suite using pytest. To run tests:

    Running Tests
    # Run the test suite
    pytest tests/
    
    # For integration tests with live API (requires configuration)
    # Set up .credentials.json or environment variables:
    # KIROKU_TEST_API_KEY, KIROKU_TEST_API_URL

    For integration tests that interact with a live KirokuForms API, you'll need to configure your API key and base URL. This is typically done via a .credentials.json file in the SDK's root directory or through environment variables (KIROKU_TEST_API_KEY, KIROKU_TEST_API_URL). Refer to examples/simple_hitl_example.py in the SDK repository for an example of credential loading.

  • Submitting Contributions: Please feel free to open issues or pull requests on the GitHub repository.

Explore More AI Integrations

: