- @if (currentThreadId) {
+ @if (currentThreadId()) {
-
Thread ID
-
- {{ currentThreadId }}
+
Thread ID
+
+ {{ currentThreadId() }}
}
@@ -62,13 +62,13 @@ export class DeploymentRuntimeComponent {
apiUrl: environment.langGraphApiUrl,
assistantId: environment.deploymentRuntimeAssistantId,
onThreadId: (id: string) => {
- this.currentThreadId = id;
+ this.currentThreadId.set(id);
},
});
- readonly apiUrl = environment.langGraphApiUrl;
- readonly assistantId = environment.deploymentRuntimeAssistantId;
- currentThreadId = '';
+ protected readonly apiUrl = environment.langGraphApiUrl;
+ protected readonly assistantId = environment.deploymentRuntimeAssistantId;
+ protected readonly currentThreadId = signal('');
send(text: string): void {
this.stream.submit({ messages: [{ role: 'human', content: text }] });
@@ -76,15 +76,15 @@ export class DeploymentRuntimeComponent {
statusBadgeBackground(): string {
const status = this.stream.status();
- if (status === 'loading') return 'rgba(0,160,80,0.12)';
- if (status === 'error') return 'rgba(200,40,40,0.1)';
- return 'rgba(0,64,144,0.08)';
+ if (status === 'loading') return 'var(--chat-success-bg, rgba(0,160,80,0.12))';
+ if (status === 'error') return 'var(--chat-error-bg, rgba(200,40,40,0.1))';
+ return 'var(--chat-accent-bg, rgba(0,64,144,0.08))';
}
statusBadgeColor(): string {
const status = this.stream.status();
- if (status === 'loading') return '#00802a';
- if (status === 'error') return '#c82828';
- return '#004090';
+ if (status === 'loading') return 'var(--chat-success-text, #4ade80)';
+ if (status === 'error') return 'var(--chat-error-text, #f87171)';
+ return 'var(--chat-accent, #60a5fa)';
}
}
diff --git a/cockpit/langgraph/durable-execution/angular/src/app/durable-execution.component.ts b/cockpit/langgraph/durable-execution/angular/src/app/durable-execution.component.ts
index 6191207e8..047325e90 100644
--- a/cockpit/langgraph/durable-execution/angular/src/app/durable-execution.component.ts
+++ b/cockpit/langgraph/durable-execution/angular/src/app/durable-execution.component.ts
@@ -1,4 +1,16 @@
// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
+/**
+ * DurableExecutionComponent demonstrates LangGraph's durable execution model.
+ *
+ * Unlike a stateless API call, a LangGraph graph persists its execution state
+ * after every node. If the server restarts mid-run, the graph resumes from the
+ * last completed node — this is "durable execution".
+ *
+ * This example visualises the pipeline steps (`analyze → plan → generate`) and
+ * tracks which step the agent is currently executing via the `step` state key.
+ * A retry button is shown when the stream enters an error state, demonstrating
+ * how `stream.reload()` re-submits the last input to resume a failed run.
+ */
import { Component, computed } from '@angular/core';
import { ChatComponent } from '@cacheplane/chat';
import { streamResource } from '@cacheplane/stream-resource';
diff --git a/cockpit/langgraph/interrupts/angular/src/app/interrupts.component.ts b/cockpit/langgraph/interrupts/angular/src/app/interrupts.component.ts
index 0c41a4b08..de14f8ab1 100644
--- a/cockpit/langgraph/interrupts/angular/src/app/interrupts.component.ts
+++ b/cockpit/langgraph/interrupts/angular/src/app/interrupts.component.ts
@@ -46,20 +46,17 @@ export class InterruptsComponent {
/**
* Handle an interrupt action from the panel.
*
- * Submitting null resumes the graph unconditionally (LangGraph convention).
- * Tier 3 will add edit/respond flows with richer resume payloads.
+ * Submitting null resumes the graph unconditionally — this is the
+ * LangGraph convention for "proceed without modification".
+ *
+ * In a production app, 'edit' would let the user modify the response
+ * before approval, and 'respond' would send a reply payload.
+ * For this demo, all actions simply resume the graph.
*/
protected onInterruptAction(action: InterruptAction): void {
- switch (action) {
- case 'accept':
- this.stream.submit(null); // Resume with approval
- break;
- case 'ignore':
- case 'respond':
- case 'edit':
- // For now, just resume — Tier 3 will add edit/respond flows
- this.stream.submit(null);
- break;
- }
+ // In a production app, 'edit' would let the user modify the response before approval.
+ // For this demo, all actions simply resume the graph.
+ void action; // Each branch intentionally does the same thing in this demo
+ this.stream.submit(null);
}
}
diff --git a/cockpit/langgraph/persistence/angular/src/app/persistence.component.ts b/cockpit/langgraph/persistence/angular/src/app/persistence.component.ts
index c83ae851b..e8f31bd01 100644
--- a/cockpit/langgraph/persistence/angular/src/app/persistence.component.ts
+++ b/cockpit/langgraph/persistence/angular/src/app/persistence.component.ts
@@ -1,4 +1,18 @@
// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
+/**
+ * PersistenceComponent demonstrates LangGraph's thread-based persistence.
+ *
+ * Each conversation is stored as a "thread" on the LangGraph backend. Threads
+ * survive page refreshes and can be resumed at any time by switching back to
+ * them. This example tracks created threads in a local signal and lets the
+ * user switch between them via the chat sidebar.
+ *
+ * Key integration points:
+ * - `threadId: null` — lets streamResource auto-create a new thread on first submit
+ * - `onThreadId` — called once the backend assigns a thread ID; used here to
+ * add the thread to the local list and set it as active
+ * - `stream.switchThread(id)` — reconnects the resource to an existing thread
+ */
import { Component, signal } from '@angular/core';
import { ChatComponent, type Thread } from '@cacheplane/chat';
import { streamResource } from '@cacheplane/stream-resource';
diff --git a/cockpit/langgraph/time-travel/angular/src/app/time-travel.component.ts b/cockpit/langgraph/time-travel/angular/src/app/time-travel.component.ts
index 0a196171b..331417862 100644
--- a/cockpit/langgraph/time-travel/angular/src/app/time-travel.component.ts
+++ b/cockpit/langgraph/time-travel/angular/src/app/time-travel.component.ts
@@ -1,4 +1,20 @@
// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
+/**
+ * TimeTravelComponent demonstrates LangGraph's checkpoint and time-travel API.
+ *
+ * Every time the agent sends a response, LangGraph saves a checkpoint — a
+ * snapshot of the full conversation state at that moment. Time travel lets
+ * you jump back to any checkpoint and continue from there.
+ *
+ * Two modes are exposed:
+ * - **Replay**: re-runs the graph from the selected checkpoint with the same
+ * input, producing the same (or a different, if non-deterministic) output.
+ * - **Fork**: sets the active branch to the checkpoint so the *next* submit()
+ * starts a new conversation branch diverging from that point.
+ *
+ * Both modes call `stream.setBranch(checkpointId)` under the hood; the
+ * difference is only conceptual and reflected in how the user interacts next.
+ */
import { Component } from '@angular/core';
import { ChatComponent, ChatTimelineSliderComponent } from '@cacheplane/chat';
import { streamResource } from '@cacheplane/stream-resource';
@@ -32,12 +48,19 @@ export class TimeTravelComponent {
assistantId: environment.streamingAssistantId,
});
+ /**
+ * Replay: sets the branch to replay from this checkpoint.
+ * The graph re-runs from this point with the same input.
+ */
protected onReplay(checkpointId: string): void {
this.stream.setBranch(checkpointId);
}
+ /**
+ * Fork: sets the branch, then the next submit() creates a new
+ * conversation branch diverging from this checkpoint.
+ */
protected onFork(checkpointId: string): void {
this.stream.setBranch(checkpointId);
- // Fork: set branch, then next submit creates a new branch from this point
}
}