Page actions

Follow a conversation

Send a message, watch partial text arrive, and wait for the response to finish.

Recorded in the real example with fixture responses paced for readability. No live model requests or narration. Actual-speed footage with reading pauses. Capture details

Read the walkthrough
  1. Enter a message and choose Send.
  2. The conversation displays partial response text while the request is active.
  3. The completed response remains in the conversation.
Download the text walkthrough

A traveler asks, “Plan a weekend in Portland.” The assistant streams an answer, looks up information, and offers an itinerary the traveler can change. Closing and reopening the page should bring that conversation back.

Each part needs a clear owner. The model chooses words and tool calls. Your backend executes the agent and saves its state. Threadplane connects that work to components you own. This guide follows one conversation through those boundaries before the individual guides add implementation detail.

The canonical chat application contains a trip-planning workflow, with Angular and native React consumers of the same Python graph. The smaller examples in these docs isolate individual capabilities. A weather card, refund approval, or local JSON renderer is a focused example; it is not another complete travel application.

Start with the parts

Architecture diagram
Application
Conversation UIMessages, composer, tool views and local draft state
Threadplane
Agent adapter / sessionAngular signals · React snapshots
Server
LangGraph runtimeModel calls, server tools and graph state
Persistence
Thread checkpointsRestore state with the confirmed thread ID
Your application owns the experience. Threadplane connects it to the agent runtime.
Read diagram description

The application renders messages, input and tools. Angular observes agent signals; React observes an application-owned session through useAgent. The Threadplane adapter sends commands and translates streamed events. LangGraph runs the model and server tools. A checkpointer stores thread state on the server. Keeping a thread ID in the browser lets the application find that state again; it does not store the conversation itself.

TermMeaning in the trip planner
Conversation or threadThe saved history containing the Portland request and later follow-ups. A thread ID identifies it.
User turnOne user intention and the agent work that answers it, including tool calls and any continuation. “Move the museum visit to Sunday” starts another turn.
RunOne execution of the backend graph. A turn can span several runs when browser tools or a human decision require a continuation.
Model callOne request from the backend to the language model. A run may call the model, execute a tool, then call the model again with the result.
HTTP requestOne network operation. Creating a thread, starting a run, and reading saved history are separate requests.
ResponseAt the network boundary, the HTTP response. In the interface, the assistant answer. A streaming HTTP response can carry many events before that answer is complete.
MessageA transcript entry with a role and identity. A user turn can produce several assistant and tool messages.
StreamThe ordered events arriving while work runs: text, tool arguments, results, state updates, and completion or failure.

This vocabulary keeps the UI honest. Receiving the first text means an answer has started; it does not mean the whole run has finished. A closed connection does not, by itself, prove that the backend stopped working.

Send an intention and observe the answer

Sequence diagram
  1. Person → ApplicationAsk for a three-day trip plan.This starts one user turn.
  2. Application → RuntimeSubmit a message on a confirmed thread.The adapter starts an agent run.
  3. Runtime → Model / toolsPlan, look up information and continue.One turn can contain several model and tool calls.
  4. Runtime → Threadplane → UIStream message and state updates.The response is built incrementally; a chunk is not a new turn.
  5. Runtime → StorageSave state when the backend supports it.LangGraph checkpoints belong to the thread.
  6. Threadplane → ApplicationShow the final answer and settled status.The next user message starts the next turn.
A turn describes the interaction. A run executes the agent. Requests carry commands and events.
Read diagram description

Read top to bottom. A person asks for a trip plan. The application submits their message to a confirmed thread. An agent run can invoke a model and tools more than once. Threadplane observes streamed message and state events and the UI updates as they arrive. Persistence depends on the backend: LangGraph can save checkpoints during execution; the AG-UI protocol alone does not provide storage. When the run settles, the UI shows the answer. The next user message starts another turn; a tool result or streamed token does not.

The application starts with a draft. Typing changes local UI state. Pressing Send accepts that draft, creates or selects a conversation, and submits a new message. The adapter then translates the response stream into the state your components read.

In the published Angular API, configure the provider once and inject the resulting agent into the component that renders it:

// app.config.ts
import type { ApplicationConfig } from '@angular/core';
import { provideAgent } from '@threadplane/langgraph';
 
export const appConfig: ApplicationConfig = {
  providers: [
    provideAgent({
      apiUrl: 'http://localhost:2024',
      assistantId: 'chat',
    }),
  ],
};
// trip-planner.component.ts
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { ChatComponent } from '@threadplane/chat';
import { injectAgent } from '@threadplane/langgraph';
 
@Component({
  selector: 'app-trip-planner',
  imports: [ChatComponent],
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `<chat [agent]="agent" />`,
})
export class TripPlannerComponent {
  protected readonly agent = injectAgent();
}

First, provideAgent identifies the backend graph; chat must be a graph key or assistant ID available on that server. Next, injectAgent retrieves that provider. Finally, <chat> submits input and renders the agent signals, including messages(), isLoading(), and error(). The provider owns the connection; the composition owns the presentation.

The React source-build API uses an application-owned session instead. Its observer is small enough to show in full:

import type { AgentSession } from '@threadplane/core';
import { useAgent } from '@threadplane/react';
 
export function ConversationText({ session }: { session: AgentSession }) {
  const snapshot = useAgent(session);
 
  return (
    <section aria-label="Conversation">
      {snapshot.messages.map((message) => (
        <p key={message.id}>{message.content}</p>
      ))}
      {snapshot.status === 'running' && <p role="status">Working…</p>}
    </section>
  );
}

useAgent reads the current snapshot and subscribes to changes. It does not create a conversation, submit a message, or dispose the session. The application that supplied session owns those actions. The React streaming example shows that owner and the richer MessageList presentation. These native React packages are private repository builds; the example is not an npm installation recipe.

A tool call adds work inside the turn

Suppose the traveler asks for a weather summary. The model can request a named tool with structured arguments. The tool implementation obtains the data, and its result returns with the same call ID. The model may then use that result to write the answer.

Server tools execute behind your backend. Browser tools execute in the application, which is useful for reading local selection or updating an application-owned view. A tool view only presents a call and its result; displaying a card is not the same as executing the tool.

A human decision is another boundary. When the backend pauses for approval, the application displays the pending decision and sends the selected response through the adapter. An approval button closing is presentation state, not proof that the requested operation completed. See Client tools and Interrupts.

Render state in components you own

The itinerary might appear as Markdown, a weather component, or a generated layout. In each case, the application chooses the renderer and the allowed components. A JSON specification describes a layout; it does not ship executable component code to the browser.

Keep three kinds of state distinct:

StateOwner and lifetime
Draft, open panels, selected tabYour Angular or React application. Usually local to the page.
Messages, tool results, itinerary factsThe backend thread, projected into current frontend signals or snapshots.
Generated layout and editable form valuesThe renderer/application, synchronized with backend state only through the actions you explicitly wire.

For example, changing the selected itinerary tab should not start a model run. Asking the agent to rearrange the itinerary should. See State management and Generative UI.

Finish, pause, fail, or stop

State diagram
idleNo active stream
runningWaiting or streaming
errorInspect recovery
idle → running
An accepted submit starts execution.
running → idle
The stream completes or is cancelled. Check for a pending interrupt.
running → error
A request or execution fails. Read the error details.
error → recovery
Retry only when allowed. Check an uncertain outcome before sending again.
Status describes execution. Inspect interrupts and error recovery before deciding what the user can do next.
Read diagram description

The public status values are idle, running and error. An accepted submit moves idle to running, including the wait before the first token. Completion or cancellation settles running back to idle. A failure moves to error. Only offer retry when the reported error allows it; an uncertain server outcome needs reconciliation. Interrupts and pending approvals are separate state, so idle alone does not mean a paused workflow has finished.

Render progress before the first token as well as while text arrives. A tool can still be running after the model has stopped producing text. A human approval can leave the conversation paused with no active text stream.

When the transport fails, use the adapter's recovery information. A read-only status check asks what happened; retrying sends work again. Those actions are different, and the application must not replay an uncertain operation merely because its response was lost.

Come back to the same conversation

Persistence has two parts: the backend retains the conversation, and the application retains a way to identify it. Saving a thread ID in a URL does not save its messages. Saving messages on the server does not automatically restore the selected thread after a browser reload.

The persistence guide connects thread IDs to checkpoints. The thread routing guide connects the selected conversation to navigation. Together, they let a traveler reopen the Portland plan and continue with “Move the museum visit to Sunday.”

Build the next part

Follow the Angular quickstart or the React streaming walkthrough, then add tools, generated UI, and saved conversations as your application needs them. Each guide explains the capability before walking the running example.

Looking for something specific?