@@ -41,7 +41,7 @@ Under the hood, agent() receives Server-Sent Events (SSE) over HTTP and feeds th
4141// Simplified view of what agent does internally:
4242
4343// 1. SSE events arrive as an observable stream
44- const messages$ = new BehaviorSubject <BaseMessage []>([]);
44+ const messages$ = new BehaviorSubject <Message []>([]);
4545const status$ = new BehaviorSubject <ResourceStatus >(' idle' );
4646
4747// 2. Each SSE chunk updates the BehaviorSubject
@@ -68,8 +68,8 @@ const chat = agent<ChatState>({
6868 assistantId: ' chat_agent' ,
6969});
7070
71- chat .messages (); // Signal<BaseMessage []>
72- chat .status (); // Signal<ResourceStatus >
71+ chat .messages (); // Signal<Message []>
72+ chat .status (); // Signal<'idle' | 'running' | 'error' >
7373chat .error (); // Signal<unknown>
7474chat .isLoading (); // Signal<boolean>
7575chat .value (); // Signal<ChatState>
@@ -84,7 +84,7 @@ The BehaviorSubject-to-Signal conversion means you get the best of both worlds:
8484
8585## The Streaming Lifecycle as Signals
8686
87- Every agent() instance moves through a lifecycle: ** idle** , ** loading ** , tokens arriving , then ** resolved ** (or ** error** ). The ` status() ` Signal reflects each transition in real time.
87+ Every agent() instance moves through a lifecycle: ** idle** , ** running ** while work is in flight , then back to ** idle ** when the stream completes (or ** error** when it fails ). The ` status() ` Signal reflects each transition in real time, while ` isLoading() ` is the convenience signal for loading UI .
8888
8989<Steps >
9090<Step title = " idle — Waiting for input" >
@@ -101,39 +101,39 @@ console.log(chat.isLoading()); // false
101101```
102102</Step >
103103
104- <Step title = " loading — Request in flight" >
105- After calling ` submit() ` , the status transitions to ` 'loading ' ` . The SSE connection is open and the agent is processing.
104+ <Step title = " running — Request in flight" >
105+ After calling ` submit() ` , the status transitions to ` 'running ' ` . The SSE connection is open and the agent is processing.
106106
107107``` typescript
108108chat .submit ({ message: ' Explain quantum computing' });
109109
110- console .log (chat .status ()); // 'loading '
110+ console .log (chat .status ()); // 'running '
111111console .log (chat .isLoading ()); // true
112112console .log (chat .messages ()); // [] (no tokens yet)
113113```
114114</Step >
115115
116- <Step title = " loading — Tokens streaming" >
117- As the agent generates tokens, the ` messages() ` Signal updates with each chunk. The status remains ` 'loading ' ` throughout.
116+ <Step title = " running — Tokens streaming" >
117+ As the agent generates tokens, the ` messages() ` Signal updates with each chunk. The status remains ` 'running ' ` throughout.
118118
119119``` typescript
120120// After first few tokens arrive:
121- console .log (chat .status ()); // 'loading ' (still streaming)
122- console .log (chat .messages ()); // [AIMessageChunk(" Quantum computing uses...") ]
121+ console .log (chat .status ()); // 'running ' (still streaming)
122+ console .log (chat .messages ()); // [{ role: 'assistant', content: ' Quantum computing uses...' } ]
123123
124124// After more tokens:
125- console .log (chat .messages ()); // [AIMessageChunk(" Quantum computing uses qubits...") ]
125+ console .log (chat .messages ()); // [{ role: 'assistant', content: ' Quantum computing uses qubits...' } ]
126126// The message content grows as tokens stream in
127127```
128128</Step >
129129
130- <Step title = " resolved — Stream complete" >
131- The agent has finished. All tokens have arrived. The status transitions to ` 'resolved ' ` .
130+ <Step title = " idle — Stream complete" >
131+ The agent has finished. All tokens have arrived. The status transitions back to ` 'idle ' ` .
132132
133133``` typescript
134- console .log (chat .status ()); // 'resolved '
134+ console .log (chat .status ()); // 'idle '
135135console .log (chat .isLoading ()); // false
136- console .log (chat .messages ()); // [AIMessage(" Quantum computing uses qubits to...") ]
136+ console .log (chat .messages ()); // [{ role: 'assistant', content: ' Quantum computing uses qubits to...' } ]
137137```
138138</Step >
139139
@@ -168,16 +168,11 @@ const lastMessage = computed(() => chat.messages().at(-1));
168168
169169// Extract just the assistant's messages
170170const assistantMessages = computed (() =>
171- chat .messages ().filter (m => m ._getType () === ' ai ' )
171+ chat .messages ().filter (m => m .role === ' assistant ' )
172172);
173173
174174// Track which tools the agent is actively calling
175- const activeTools = computed (() =>
176- chat .messages ()
177- .filter (m => m ._getType () === ' ai' )
178- .flatMap (m => m .tool_calls ?? [])
179- .filter (tc => ! tc .result )
180- );
175+ const activeTools = computed (() => chat .toolCalls ());
181176
182177// Build a user-facing error message
183178const errorDisplay = computed (() => {
@@ -195,7 +190,7 @@ const errorDisplay = computed(() => {
195190const viewModel = computed (() => ({
196191 messages: chat .messages (),
197192 isStreaming: chat .isLoading (),
198- canSend: chat .status () !== ' loading ' ,
193+ canSend: ! chat .isLoading () ,
199194 messageCount: messageCount (),
200195 error: errorDisplay (),
201196}));
@@ -238,10 +233,10 @@ effect(() => {
238233// Track streaming duration for performance monitoring
239234effect (() => {
240235 const status = chat .status ();
241- if (status === ' loading ' ) {
236+ if (status === ' running ' ) {
242237 this .streamStart = performance .now ();
243238 }
244- if (status === ' resolved ' && this .streamStart ) {
239+ if (status === ' idle ' && this .streamStart ) {
245240 const duration = performance .now () - this .streamStart ;
246241 this .analytics .track (' stream_duration_ms' , { duration });
247242 this .streamStart = null ;
@@ -267,7 +262,7 @@ import { agent } from '@ngaf/langgraph';
267262 template: `
268263 <!-- Status bar -->
269264 @switch (chat.status()) {
270- @case ('loading ') {
265+ @case ('running ') {
271266 <div class="status-bar streaming">
272267 Agent is responding...
273268 </div>
@@ -283,18 +278,18 @@ import { agent } from '@ngaf/langgraph';
283278 <!-- Message list -->
284279 <div class="messages" #chatContainer>
285280 @for (message of chat.messages(); track $index) {
286- @switch (message._getType() ) {
287- @case ('human ') {
281+ @switch (message.role ) {
282+ @case ('user ') {
288283 <div class="message user">
289284 {{ message.content }}
290285 </div>
291286 }
292- @case ('ai ') {
287+ @case ('assistant ') {
293288 <div class="message assistant">
294289 {{ message.content }}
295290
296291 <!-- Show tool calls if the assistant invoked any -->
297- @for (tool of message.tool_calls ?? [] ; track tool.id) {
292+ @for (tool of chat.toolCalls() ; track tool.id) {
298293 <div class="tool-call">
299294 Called: {{ tool.name }}
300295 </div>
@@ -460,14 +455,14 @@ import { agent } from '@ngaf/langgraph';
460455 changeDetection: ChangeDetectionStrategy .OnPush ,
461456 template: `
462457 @for (msg of chat.messages(); track $index) {
463- @switch (msg._getType() ) {
464- @case ('human ') {
458+ @switch (msg.role ) {
459+ @case ('user ') {
465460 <div class="user">{{ msg.content }}</div>
466461 }
467- @case ('ai ') {
462+ @case ('assistant ') {
468463 <div class="assistant">
469464 {{ msg.content }}
470- @for (tc of msg.tool_calls ?? [] ; track tc.id) {
465+ @for (tc of chat.toolCalls() ; track tc.id) {
471466 <span class="tool-badge">{{ tc.name }}</span>
472467 }
473468 </div>
@@ -491,7 +486,7 @@ export class ChatComponent {
491486 // Derived state from the Python agent's output
492487 toolsUsed = computed (() =>
493488 this .chat .messages ()
494- .filter (m => m ._getType () === ' tool' )
489+ .filter (m => m .role === ' tool' )
495490 .map (m => m .name )
496491 );
497492
0 commit comments