CopilotKit

Exiting the agent loop


After your agent has finished a workflow, you'll usually want to explicitly end that loop by calling the CopilotKit exit method in your agent code.

Exiting the agent has different effects depending on mode:

  • Router Mode: Exiting the agent hands responsibility for handling input back to the router, which can initiate chat, call actions, other agents, etc. The router can return to this agent later (starting a new loop) to satisfy a user request.

  • Agent Lock Mode: Exiting the agent restarts the workflow loop for the current agent.

In this example from our email-sending app, the send_email node explicitly exits, then manually sends a response back to the user as a ToolMessage:

Install the CopilotKit SDK#

Any LangGraph agent can be used with CopilotKit. However, creating deep agentic experiences with CopilotKit requires our LangGraph SDK.

uv add copilotkit
poetry add copilotkit
pip install copilotkit --extra-index-url https://copilotkit.gateway.scarf.sh/simple/
conda install copilotkit -c copilotkit-channel

npm npm install @copilotkit/sdk-js

Exit the agent loop#

This will exit the agent session as soon as the current LangGraph run is finished, either by a breakpoint or by reaching the END node.

from copilotkit.langgraph import (copilotkit_exit)
# ...
async def send_email_node(state: EmailAgentState, config: RunnableConfig):
    """Send an email."""

    await copilotkit_exit(config) 

    # get the last message and cast to ToolMessage
    last_message = cast(ToolMessage, state["messages"][-1])
    if last_message.content == "CANCEL":
        return {
            "messages": [AIMessage(content="❌ Cancelled sending email.")],
        }
    else:
        return {
            "messages": [AIMessage(content="✅ Sent email.")],
        }

// ...

async function sendEmailNode(state: EmailAgentState, config: RunnableConfig): Promise<{ messages: any[] }> {
    // Send an email.

    await copilotkitExit(config); 

    // get the last message and cast to ToolMessage
    const lastMessage = state.messages[state.messages.length - 1] as ToolMessage;
    if (lastMessage.content === "CANCEL") {
        return {
            messages: [new AIMessage(content="❌ Cancelled sending email.")],
        }
    } else {
        return {
            messages: [new AIMessage(content="✅ Sent email.")],
        }
    }
}