Skip to lesson

NVIDIANVIDIA-Certified Professional: Agentic AI2026-102.5 Develop dynamic conversation flows with real-time streaming and feedback mechanisms.

Recover dialog turns from execution state

Completed by the knowledge check

Learning objective

Manage multi-turn dialog state, interruptions, reconnections, and state recovery.

Core idea

A multi-turn conversation has at least three lifecycles: the conversation, the workflow execution answering a turn, and the connection displaying it. A connection can disappear while work continues. A user can correct a turn while an earlier response is still arriving. Recover by locating the execution and its saved state before deciding what to repeat.

See 1.4.1 for memory-store roles and 1.8.3 for shared state across replicas. This lesson adds the dialog decisions: which turn owns output, how interruption changes the active execution, and what the client does after losing contact.

How it works

Keep the identifiers separate

A WebSocket user_message carries the dialog in content.messages: user and assistant messages followed by the next user message. The schema says the running workflow processes only the last user message. In this integration, the client retains and resends the transcript each turn; conversation_id groups messages and parent_id associates updates with their originating message.

Interactive HTTP exposes an execution identity when the workflow pauses for human input or OAuth. Store that execution_id and, for a 202 response, the returned status_url; typed SSE pause events carry the ID but not status_url. An ordinary stream that drops before any pause has no documented execution ID to query. The API reference marks versioned HTTP paths experimental because of HITL and OAuth support.

For a LangGraph workflow, configurable.thread_id selects the checkpointed thread. LangGraph checkpointers persist graph state at super-step boundaries and support conversation continuity. An application’s association between this thread and the toolkit’s conversation or execution IDs is integration logic; create it explicitly rather than assuming similarly named identifiers are interchangeable.

NVIDIA documents integration through workflow._type: langgraph_wrapper and the graph configuration field, with the nvidia-nat-langchain package. That wrapper brings an existing graph into the toolkit. Choose and configure the graph’s persistence and dialog behavior as part of that integration; the wrapper’s configuration is not itself a checkpoint policy.

Advance a turn from committed state

The Checkpointers page describes sending conversational follow-up messages as ordinary input to the same thread. The Interrupts page reserves Command(resume=...) for answering a pending interrupt(). Select the input operation according to whether this is another turn or the response to a suspended one.

Inspect saved state with graph.get_state(config) when rebuilding the display. A StateSnapshot contains saved values and execution information. Render committed history from that state and keep any partially displayed answer labelled incomplete until the execution outcome is reconciled. A user’s copied transcript can help explain their intent, but it cannot establish which graph step completed.

Checkpoint retention and context selection remain the memory owner’s responsibility; see 1.4.2. Here the essential property is stable thread selection. Replacing thread_id during reconnect creates a different thread instead of locating the saved one. Using an in-memory saver also cannot satisfy recovery across a process restart; choose a durable checkpointer for that requirement.

Treat interruptions as explicit control changes

A planned pause uses LangGraph interrupt() and persists state through a checkpointer. Resume it with Command(resume=...) and the same thread ID. The interrupted node restarts from its beginning, so statements before the interrupt execute again. Keep a non-repeatable tool write out of that replayed prefix, or use the tool’s own duplicate-prevention mechanism. A pause is not a rollback of actions already performed.

A user pressing Stop is a different event from a planned prompt. In an application-owned Python execution task, asyncio.Task.cancel() requests cancellation; CancelledError is raised at the next opportunity. Use try/finally for cleanup and generally propagate CancelledError after cleanup. These are Python task semantics, not an assertion that a toolkit HTTP connection automatically cancels every downstream operation.

While cancellation is being resolved, retain the previous turn’s identity and reject its output from the replacement turn’s answer buffer. If a remote action has already completed, cancellation cannot make it un-happen. The workflow owner must reconcile that result before accepting a replacement action. See 1.5.2 for the broader specialist execution lifecycle.

Reconnect by querying, then decide

After a pause has exposed an interactive execution ID, query GET /executions/{execution_id}. Its lifecycle distinguishes running, interaction_required, oauth_required, completed, and failed. Rebuild the client around that status rather than resubmitting the original request.

If interaction is required, display the pending prompt and submit to its response_url. A response accepted with 204 returns the execution to running. A 400 indicates an interaction already resolved; a 404 means the execution or interaction was not found. Refresh status instead of repeatedly submitting a stale response. These routes require a locatable interactive execution, not merely a conversation ID.

For a token stream lost before any pause, reconcile the durable LangGraph thread through your integration. GET /executions/{execution_id} is unavailable without an ID. Inspect the saved graph position and any unresolved external action before starting replacement work, including after a process restart.

If reconnectable job identity matters more than token streaming, submit the turn through /v1/workflow/async with a client-chosen job_id. Repeating that submission with an existing ID returns the job’s status. This endpoint is non-streaming, requires nvidia-nat[async_endpoints], and defaults sync_timeout to 0, returning immediately. See 1.8.3 for Dask and persistent --db_url deployment; here the deciding trade-off is known job identity versus live token delivery.

Key distinctions and trade-offs

Choose the recovery mechanism by what was interrupted:

SituationMechanismTrade-off
Another WebSocket turnClient-held history resent in content.messagesRestores dialog context, not graph execution position
Connection lost after an HTTP pauseRetained execution_id and status GETRequires a pause that exposed the ID and a locatable execution
Stream lost before a pauseDurable graph thread reconciliationKeeps token streaming but requires a recovery integration
Reconnectable background turn/v1/workflow/async with a client-chosen job_idGives queryable job identity while giving up token streaming
Planned human pauseinterrupt() and Command(resume=...) on the same threadThe interrupted node’s prefix runs again
User stops active workApplication task cancellation and outcome reconciliationExternal effects need separate accounting
Process restartDurable checkpointer and configurable.thread_idAdds persistence operations and retention ownership

A transcript restores conversational context. A checkpoint restores graph state. Neither should be mistaken for proof that a remote tool action was rolled back.

Architecture / decision scenario

A manufacturing procurement assistant lets buyers refine a supplier search over several turns. The business requires recovery after a service restart and allows at most one supplier-contact request for each buyer decision. In a test, the connection drops after a tool has recorded a contact request but before the final 120-word answer finishes.

A developer proposes reconnecting and replaying the complete transcript into a fresh thread. The workflow owner rejects that design: it can repeat the contact action and loses the recorded graph position. Because this stream had not paused, no interactive execution ID was returned. The integration retains the conversation-to-thread association and inspects the durable graph state and the tool’s recorded result before continuing, after either network loss or restart. Switching to /v1/workflow/async with a chosen job_id would give a different reconnect path, but the team keeps streaming and owns graph-based reconciliation.

The buyer then interrupts to change the delivery date. The application requests cancellation of active local work and keeps late output attached to the superseded turn. A planned clarification later uses interrupt() and resumes on the same thread. The team places the supplier-contact write outside the interrupted node’s replayed prefix. The owning team tests both a 3-second network loss and a process restart; those failures exercise different mechanisms despite producing a similar frozen chat screen.

Common exam traps

  • Replacing the thread ID when reconnecting to an existing conversation.
  • Treating a transcript as an execution checkpoint.
  • Using Command(resume=...) for every ordinary follow-up message.
  • Assuming code before interrupt() runs only once.
  • Treating cancellation as immediate or as rollback of remote writes.
  • Assuming every interrupted token stream supplies an interactive execution ID.

What to remember

Preserve conversation, turn, execution, and checkpoint identities separately. Reconcile execution status after connection loss, use durable checkpoints for restart recovery, and distinguish planned pauses from cancellation. Recovery is complete only when the display and workflow agree about which turn owns the outcome.

Knowledge check

References