Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,18 @@ chat.messages(); // Signal<Message[]>
chat.status(); // Signal<'idle' | 'running' | 'error'>
chat.error(); // Signal<unknown>
chat.isLoading(); // Signal<boolean>

// LangGraph-specific (see note below):
chat.value(); // Signal<ChatState>
```

</Tab>
</Tabs>

<Callout type="info" title="`value()` is LangGraph-specific">
`messages()`, `status()`, `error()`, and `isLoading()` are part of the runtime-neutral `Agent` contract that every adapter implements. `value()` is not — it lives on `LangGraphAgent`. You can call `chat.value()` here only because `injectAgent()` returns a `LangGraphAgent`. Code written against the neutral `Agent` contract should read agent state through `state()` rather than `value()`.
</Callout>

<Callout type="tip" title="Why this matters">
The BehaviorSubject-to-Signal conversion means you get the best of both worlds: RxJS handles the async SSE transport (reconnection, backpressure, error recovery), while Signals handle the synchronous UI reactivity (change detection, template binding, computed derivations). You only interact with the Signal side.
</Callout>
Expand Down
39 changes: 26 additions & 13 deletions apps/website/content/docs/langgraph/concepts/langgraph-basics.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -317,32 +317,45 @@ Templates re-render automatically via OnPush change detection
<Tab label="Signal mapping">

```typescript
// Every LangGraph concept maps to a Signal:
// Every LangGraph concept maps to a Signal. The surface splits into two
// groups: the runtime-neutral `Agent` contract that every adapter implements,
// and LangGraph-specific signals added by `LangGraphAgent`.

// Agent state values
agent.value() // Signal<T> — full state object

// Conversation
// ── Signals on every agent (the `Agent` contract) ───────────────────────────
agent.messages() // Signal<Message[]> — runtime-neutral message history

// Lifecycle
agent.status() // Signal<AgentStatus> — idle/running/error
agent.isLoading() // Signal<boolean> — is the agent running?
agent.error() // Signal<unknown> — last error, if any
agent.toolCalls() // Signal<ToolCall[]> — tool call progress and results
agent.state() // Signal<Record<string, unknown>> — runtime-neutral state

// Human-in-the-loop
agent.interrupt() // Signal<Interrupt> — agent is paused
// Optional on the contract (present when the runtime supports them):
agent.interrupt() // Signal<AgentInterrupt | undefined> — agent is paused
agent.subagents() // Signal<Map<string, Subagent>> — delegated agents

// Debugging
// ── AgentWithHistory sub-contract (adapters with checkpoint history) ─────────
// Not on the bare `Agent` — request/response adapters omit it. The type and
// signal are runtime-neutral; LangGraph implements this sub-contract.
agent.history() // Signal<AgentCheckpoint[]> — runtime-neutral timeline

// ── LangGraph-specific signals (`LangGraphAgent`) ───────────────────────────
// These exist because injectAgent() returns a LangGraphAgent. They are NOT
// part of the runtime-neutral Agent contract.
agent.value() // Signal<T> — full LangGraph state object
agent.hasValue() // Signal<boolean> — true once any value/message arrived
agent.langGraphHistory() // Signal<ThreadState[]> — raw LangGraph checkpoints
agent.branch() // Signal<string> — time-travel branch
agent.experimentalBranchTree() // Signal<AgentBranchTree<T>> — branch tree

agent.toolCalls() // Signal<ToolCall[]> — tool call progress and results
agent.toolProgress() // Signal<ToolProgress[]> — in-flight tool progress
agent.queue() // Signal<AgentQueue> — pending enqueue runs
agent.subagents() // Signal<Map<string, Subagent>> — delegated agents
agent.activeSubagents() // Signal<SubagentStreamRef[]> — running subagents
agent.customEvents() // Signal<CustomStreamEvent[]> — raw custom events
```

The base `subagents()` signal exposes a `Map<string, Subagent>` from the
runtime-neutral contract, while LangGraph adds `activeSubagents()` — a filtered
`SubagentStreamRef[]` containing only subagents whose status is `running`.

</Tab>
</Tabs>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ Incoming SSE chunks are parsed and pushed into BehaviorSubjects — one per sign
</Step>
<Step title="`injectAgent()` converts to Signals">
BehaviorSubjects are converted to Angular Signals via `toSignal()`. Every update triggers Angular's change detection automatically.

By default, signal updates are throttled to roughly one update per animation frame (~16ms) so a burst of SSE tokens batches into a single change-detection pass instead of a storm. The `messages` signal is exempt — it propagates token-by-token so streaming text renders as it arrives. Tune or disable throttling with the `throttle?: number | false` option on `provideAgent`.
</Step>
<Step title="Templates re-render">
Components using `OnPush` re-render only when signal values change. No manual `detectChanges()`, no zone triggers, no subscriptions to manage.
Expand Down
Loading