CopilotKit

Pausing the Agent for Input

Pause an agent run mid-tool, hand control to a custom React component, and resume with the user's answer.


"""CrewAI scheduling crew for the interrupt-adapted demos.Powers both gen-ui-interrupt and interrupt-headless. The LangGraph referenceuses `interrupt()` with checkpoint/resume; CrewAI has no equivalent primitive,so we adapt via Strategy B: the backend crew defines a system prompt thatinstructs the chat LLM to call `schedule_meeting`, and the frontend registersthat tool via `useFrontendTool` with an async handler that renders atime-picker and returns a Promise that only resolves when the user picks aslot (or cancels).No backend tools — `schedule_meeting` is satisfied entirely by the frontend."""from __future__ import annotationsfrom crewai import Agent, Crew, Process, Taskfrom agents._chat_flow_helpers import preseed_system_promptCREW_NAME = "InterruptSchedulingCrew"_SYSTEM_PROMPT = (    "You are a scheduling assistant. Whenever the user asks you to book a call "    "or schedule a meeting, you MUST call the `schedule_meeting` tool. Pass a "    "short `topic` describing the purpose of the meeting and, if known, an "    "`attendee` describing who the meeting is with.\n\n"    "The `schedule_meeting` tool is implemented on the client: it surfaces a "    "time-picker UI to the user and returns the user's selection. After the "    "tool returns, briefly confirm whether the meeting was scheduled and at "    "what time, or note that the user cancelled. Do NOT ask for approval "    "yourself — always call the tool and let the picker handle the decision.\n\n"    "Keep responses short and friendly. After you finish executing tools, "    "always send a brief final assistant message summarizing what happened so "    "the message persists.")preseed_system_prompt(CREW_NAME, _SYSTEM_PROMPT)def _build_crew() -> Crew:    agent = Agent(        role="Scheduling Assistant",        goal="Help users schedule meetings by calling the schedule_meeting tool",        backstory=(            "You are a concise scheduling assistant. You always call "            "schedule_meeting when asked to book or schedule anything."        ),        verbose=False,        tools=[],    )    task = Task(        description=(            "Help the user schedule a meeting by calling the schedule_meeting "            "frontend tool."        ),        expected_output="A confirmation of the scheduled meeting or cancellation.",        agent=agent,    )    return Crew(        name=CREW_NAME,        agents=[agent],        tasks=[task],        process=Process.sequential,        verbose=False,        chat_llm="gpt-4o",    )_cached_crew: Crew | None = Noneclass InterruptScheduling:    """Adapter matching the shape `add_crewai_crew_fastapi_endpoint` expects."""    name: str = CREW_NAME    def crew(self) -> Crew:        global _cached_crew        if _cached_crew is None:            _cached_crew = _build_crew()        return _cached_crew

What is this?#

useInterrupt lets your agent pause mid-run, hand control to the user through a custom React component, and resume with whatever the user returns. How that pause is implemented depends on the framework's runtime.

Not available on this framework. useInterrupt is only meaningful when the underlying runtime exposes either a native interrupt(...) primitive (LangGraph) or a Promise-resolving frontend tool path (Microsoft Agent Framework). For all other integrations, use useHumanInTheLoop instead — it's the standard hook for tool-call-based pause/resume flows and works on every framework that supports tool calls.

When should I use this?#

Reach for useInterrupt when the pause is a graph-enforced checkpoint where the code path must stop and wait for a human, not an LLM-initiated tool call. Typical cases:

  • A sensitive action (payments, irreversible writes) must be approved
  • A required piece of state isn't known and can only be collected from the user
  • The agent explicitly reaches an approval node in a longer workflow
  • You want the server-side contract to be interrupt(...) and resume with a payload

For LLM-initiated pauses where the model decides on the fly to ask the user, prefer useHumanInTheLoop.

Going further#