|
1 | | -import { Component, computed } from '@angular/core'; |
2 | | -import { ChatComponent } from '@cacheplane/chat'; |
| 1 | +import { Component } from '@angular/core'; |
| 2 | +import { ChatComponent, views } from '@cacheplane/chat'; |
| 3 | +import { signalStateStore } from '@cacheplane/render'; |
3 | 4 | import { agent } from '@cacheplane/angular'; |
4 | 5 | import { environment } from '../environments/environment'; |
| 6 | +import { PlanChecklistComponent } from './views/plan-checklist.component'; |
| 7 | +import { CheckboxRowComponent } from './views/checkbox-row.component'; |
5 | 8 |
|
6 | | -/** |
7 | | - * Represents a single step in an agent-generated plan. |
8 | | - */ |
9 | | -interface PlanStep { |
10 | | - title: string; |
11 | | - status: 'pending' | 'in_progress' | 'complete'; |
12 | | -} |
13 | | - |
14 | | -/** |
15 | | - * PlanningComponent demonstrates agent task decomposition. |
16 | | - * |
17 | | - * The agent receives a complex task, breaks it into ordered steps, |
18 | | - * and executes them sequentially. The sidebar displays a live checklist |
19 | | - * of plan steps derived from the `plan` array in the graph state. |
20 | | - * |
21 | | - * Key integration points: |
22 | | - * - `stream.value()` exposes the full graph state, including the `plan` array |
23 | | - * - `planSteps` is derived from `stream.value()` for reactive sidebar rendering |
24 | | - * - Step status icons update in real time as the agent progresses |
25 | | - */ |
26 | 9 | @Component({ |
27 | 10 | selector: 'app-planning', |
28 | 11 | standalone: true, |
29 | 12 | imports: [ChatComponent], |
30 | | - template: ` |
31 | | - <div class="flex h-screen"> |
32 | | - <chat [ref]="stream" class="flex-1 min-w-0" /> |
33 | | - <aside class="w-72 shrink-0 border-l overflow-y-auto p-4 space-y-2" |
34 | | - style="border-color: var(--chat-border, #333); background: var(--chat-bg, #171717); color: var(--chat-text, #e0e0e0);"> |
35 | | - <h3 class="text-xs font-semibold uppercase tracking-wide" |
36 | | - style="color: var(--chat-text-muted, #777);">Plan</h3> |
37 | | - @if (planSteps().length === 0) { |
38 | | - <p class="text-sm italic" style="color: var(--chat-text-muted, #777);">No plan yet</p> |
39 | | - } |
40 | | - @for (step of planSteps(); track step.title) { |
41 | | - <div class="flex items-start gap-2 rounded-md px-2 py-1.5 text-sm" |
42 | | - style="background: var(--chat-bg-hover, #222);"> |
43 | | - <span class="mt-0.5 shrink-0 text-base leading-none"> |
44 | | - @if (step.status === 'complete') { |
45 | | - <span style="color: #22c55e;">✓</span> |
46 | | - } @else if (step.status === 'in_progress') { |
47 | | - <span class="inline-block animate-spin" style="color: var(--chat-text-muted, #777);">◠</span> |
48 | | - } @else { |
49 | | - <span style="color: var(--chat-border-light, #444);">○</span> |
50 | | - } |
51 | | - </span> |
52 | | - <span style="color: var(--chat-text, #e0e0e0);">{{ step.title }}</span> |
53 | | - </div> |
54 | | - } |
55 | | - </aside> |
56 | | - </div> |
57 | | - `, |
| 13 | + template: `<chat [ref]="stream" [views]="ui" [store]="uiStore" class="block h-screen" />`, |
58 | 14 | }) |
59 | 15 | export class PlanningComponent { |
60 | | - /** |
61 | | - * The streaming resource connected to the planning graph. |
62 | | - * |
63 | | - * The graph returns a `plan` array alongside messages in its state. |
64 | | - * Each plan entry has a `title` and `status` that drive the sidebar checklist. |
65 | | - */ |
66 | 16 | protected readonly stream = agent({ |
67 | 17 | apiUrl: environment.langGraphApiUrl, |
68 | | - assistantId: environment.streamingAssistantId, |
| 18 | + assistantId: environment.planningAssistantId, |
69 | 19 | }); |
70 | 20 |
|
71 | | - /** |
72 | | - * Reactive list of plan steps derived from the graph state. |
73 | | - * |
74 | | - * The Python graph stores the plan as `state.plan` — an array of objects |
75 | | - * with `title` and `status` fields. This signal re-computes whenever |
76 | | - * the stream state changes. |
77 | | - */ |
78 | | - protected readonly planSteps = computed<PlanStep[]>(() => { |
79 | | - const val = this.stream.value() as Record<string, unknown>; |
80 | | - const plan = val?.['plan']; |
81 | | - if (!Array.isArray(plan)) return []; |
82 | | - return plan.map((step: Record<string, unknown>) => ({ |
83 | | - title: String(step['title'] ?? ''), |
84 | | - status: (['pending', 'in_progress', 'complete'].includes(step['status'] as string) |
85 | | - ? step['status'] |
86 | | - : 'pending') as PlanStep['status'], |
87 | | - })); |
| 21 | + readonly ui = views({ |
| 22 | + 'plan-checklist': PlanChecklistComponent, |
| 23 | + 'checkbox-row': CheckboxRowComponent, |
88 | 24 | }); |
| 25 | + |
| 26 | + readonly uiStore = signalStateStore({}); |
89 | 27 | } |
0 commit comments