Skip to main content
Build sophisticated, stateful AI workflows using LangGraph’s graph-based architecture with easy access to business data through StackOne’s Tools.

Overview

  • Stateful workflows with persistent memory
  • Graph-based execution with conditional branching
  • Multi-step processes with human-in-the-loop
  • Dynamic tool loading based on connected accounts
from stackone_ai import StackOneToolSet
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI

def create_agent_for_account(account_id: str):
    """
    Create LangGraph agent with tools for a specific account.

    In production, account_id comes from:
    - User/tenant context
    - Authentication middleware
    - Request parameters
    """
    # Initialize toolset
    toolset = StackOneToolSet()

    # Fetch tools dynamically for this account
    tools = toolset.fetch_tools(account_ids=[account_id])

    # Convert to LangChain format (LangGraph uses LangChain tools)
    langchain_tools = tools.to_langchain()

    # Create LangGraph agent
    model = ChatOpenAI(model="gpt-5")
    agent = create_react_agent(model, langchain_tools)

    return agent

# Use the agent
account_id = get_current_user_account()  # Your function
agent = create_agent_for_account(account_id)

# Run stateful workflow
config = {"configurable": {"thread_id": "user-123-session"}}
result = agent.invoke(
    {"messages": [("user", "Find all employees hired this year")]},
    config=config
)

Dynamic Tool Loading

Load tools based on available integrations:
from stackone_ai import StackOneToolSet

toolset = StackOneToolSet()

# Load all tools for specific accounts (most common)
tools = toolset.fetch_tools(
    account_ids=["account-123"]
)

# Optional: Filter by operation type for security
tools = toolset.fetch_tools(
    account_ids=["account-123"],
    actions=["*_list_*", "*_get_*"]  # Only list and get operations (read-only)
)

# Convert to LangChain format for LangGraph
langchain_tools = tools.to_langchain()

Stateful Workflows

Build complex, multi-step workflows with state:
from langgraph.graph import StateGraph, MessagesState
from langgraph.prebuilt import ToolNode
from langchain_core.messages import SystemMessage

# Define workflow state
class AgentState(MessagesState):
    employee_data: dict
    analysis_complete: bool

# Create graph
def create_hr_workflow(account_id: str):
    toolset = StackOneToolSet()
    tools = toolset.fetch_tools(account_ids=[account_id])
    langchain_tools = tools.to_langchain()

    # Create nodes
    tool_node = ToolNode(langchain_tools)

    def agent_node(state: AgentState):
        model = ChatOpenAI(model="gpt-5")
        model_with_tools = model.bind_tools(langchain_tools)

        messages = [
            SystemMessage(content="You are an HR data analyst."),
            *state["messages"]
        ]
        response = model_with_tools.invoke(messages)
        return {"messages": [response]}

    # Build graph
    workflow = StateGraph(AgentState)
    workflow.add_node("agent", agent_node)
    workflow.add_node("tools", tool_node)

    workflow.set_entry_point("agent")
    workflow.add_conditional_edges("agent", should_continue)
    workflow.add_edge("tools", "agent")

    return workflow.compile()

def should_continue(state: AgentState):
    """Decide whether to continue or finish"""
    last_message = state["messages"][-1]
    if not last_message.tool_calls:
        return "end"
    return "tools"

Human-in-the-Loop

Add human approval for sensitive operations:
from langgraph.prebuilt import create_react_agent
from langgraph.checkpoint.memory import MemorySaver

def create_hr_agent_with_approval(account_id: str):
    toolset = StackOneToolSet()

    # Only expose read operations initially (optional filtering)
    read_tools = toolset.fetch_tools(
        account_ids=[account_id],
        actions=["*_list_*", "*_get_*"]  # Only list and get operations
    )

    agent = create_react_agent(
        model=ChatOpenAI(model="gpt-5"),
        tools=read_tools.to_langchain(),
        checkpointer=MemorySaver()
    )

    return agent

# Usage: Approve before executing sensitive actions
agent = create_hr_agent_with_approval(account_id)
result = agent.invoke({"messages": [("user", "Review termination candidates")]})

# Human reviews, then loads write tools for approved actions
if user_approves(result):
    write_tools = toolset.fetch_tools(
        account_ids=[account_id],
        actions=["*_update_*", "*_create_*"]  # Only update and create operations
    )
    # Continue with write operations...

Best Practices

Account ID from Context

# Get from request context
account_id = get_account_from_auth_token(request)
account_id = tenant.stackone_account_id

Tool Security

# Optional: Only expose read operations for security
safe_tools = toolset.fetch_tools(
    account_ids=[account_id],
    actions=["*_list_*", "*_get_*"]  # Only list and get operations
)

# Or exclude dangerous operations
safe_tools = toolset.fetch_tools(
    account_ids=[account_id],
    actions=["!*_delete_*"]  # Everything except deletes
)

Next Steps