1- import React from 'react'
1+ import React , { useState , useEffect } from 'react'
22import { Meta } from '@storybook/react'
33import { ChatInterfaceMessages } from './ChatInterfaceMessages'
44import { type ChatMessageProps } from './ChatMessage'
@@ -28,36 +28,99 @@ export const TextOnly: Story = {
2828 {
2929 id : '1' ,
3030 role : 'user' ,
31- message : { type : 'text' , data : { content : 'Hello! ' } } ,
31+ message : { type : 'text' , data : { content : 'Hello, I need help with my laptop. ' } } ,
3232 } ,
3333 {
3434 id : '2' ,
3535 role : 'assistant' ,
36- message : { type : 'text' , data : { content : 'Hi there! How can I assist you today ?' } } ,
37- currentUser : { name : 'Robot ' } ,
36+ message : { type : 'text' , data : { content : 'Hi! I can help you with that. Can you describe the issue ?' } } ,
37+ currentUser : { name : 'Helpdesk Bot ' } ,
3838 } ,
3939 {
4040 id : '3' ,
4141 role : 'user' ,
42- message : { type : 'text' , data : { content : 'What is our leave policy? ' } } ,
42+ message : { type : 'text' , data : { content : 'It keeps restarting randomly. ' } } ,
4343 } ,
4444 {
4545 id : '4' ,
4646 role : 'assistant' ,
47- message : { type : 'forwarding' , data : { content : 'HR ' } } ,
47+ message : { type : 'forwarding' , data : { content : 'IT Support ' } } ,
4848 } ,
4949 {
5050 id : '5' ,
5151 role : 'assistant' ,
52- message : { type : 'text' , data : { content : 'Its blah blah blah ' } } ,
52+ message : { type : 'text' , data : { content : 'I have created a support ticket for you. ' } } ,
5353 } ,
5454 {
5555 id : '6' ,
5656 role : 'assistant' ,
5757 message : {
5858 type : 'tool-invocation' ,
5959 data : {
60- id : 1 ,
60+ id : 101 ,
61+ tool : 'task' ,
62+ system : 'payload' ,
63+ fetchLatest : false ,
64+ taskData : chatCardTestData ,
65+ } ,
66+ } ,
67+ } ,
68+ {
69+ id : '7' ,
70+ role : 'user' ,
71+ message : { type : 'text' , data : { content : 'Thank you! Can I also get help with my printer?' } } ,
72+ } ,
73+ {
74+ id : '8' ,
75+ role : 'assistant' ,
76+ message : { type : 'text' , data : { content : 'Of course! What issue are you facing with the printer?' } } ,
77+ } ,
78+ {
79+ id : '9' ,
80+ role : 'user' ,
81+ message : { type : 'text' , data : { content : 'It is not connecting to the network.' } } ,
82+ } ,
83+ {
84+ id : '10' ,
85+ role : 'assistant' ,
86+ message : { type : 'forwarding' , data : { content : 'Printer Support' } } ,
87+ } ,
88+ {
89+ id : '11' ,
90+ role : 'assistant' ,
91+ message : { type : 'text' , data : { content : 'A new ticket has been created for your printer issue.' } } ,
92+ } ,
93+ {
94+ id : '12' ,
95+ role : 'assistant' ,
96+ message : {
97+ type : 'tool-invocation' ,
98+ data : {
99+ id : 102 ,
100+ tool : 'task' ,
101+ system : 'payload' ,
102+ fetchLatest : false ,
103+ taskData : chatCardTestData ,
104+ } ,
105+ } ,
106+ } ,
107+ {
108+ id : '13' ,
109+ role : 'user' ,
110+ message : { type : 'text' , data : { content : 'How do I check the status of my tickets?' } } ,
111+ } ,
112+ {
113+ id : '14' ,
114+ role : 'assistant' ,
115+ message : { type : 'text' , data : { content : 'You can view all your open tickets in the Helpdesk portal.' } } ,
116+ } ,
117+ {
118+ id : '15' ,
119+ role : 'assistant' ,
120+ message : {
121+ type : 'tool-invocation' ,
122+ data : {
123+ id : 103 ,
61124 tool : 'task' ,
62125 system : 'payload' ,
63126 fetchLatest : false ,
@@ -76,3 +139,190 @@ export const TextOnly: Story = {
76139 } ,
77140 } ,
78141}
142+
143+ export const StreamingChunks : Story = {
144+ render : ( args ) => {
145+ const [ messages , setMessages ] = useState < ChatMessageProps [ ] > ( [ ] )
146+ useEffect ( ( ) => {
147+ const convo : ChatMessageProps [ ] = [
148+ {
149+ id : '1' ,
150+ role : 'user' ,
151+ message : { type : 'text' , data : { content : 'Hello, I need help with my laptop.' } } ,
152+ } ,
153+ {
154+ id : '2' ,
155+ role : 'assistant' ,
156+ message : { type : 'text' , data : { content : '' } } ,
157+ currentUser : { name : 'Helpdesk Bot' } ,
158+ } ,
159+ {
160+ id : '3' ,
161+ role : 'user' ,
162+ message : { type : 'text' , data : { content : 'It keeps restarting randomly.' } } ,
163+ } ,
164+ {
165+ id : '4' ,
166+ role : 'assistant' ,
167+ message : { type : 'forwarding' , data : { content : 'IT Support' } } ,
168+ } ,
169+ {
170+ id : '5' ,
171+ role : 'assistant' ,
172+ message : { type : 'text' , data : { content : '' } } ,
173+ } ,
174+ {
175+ id : '6' ,
176+ role : 'assistant' ,
177+ message : {
178+ type : 'tool-invocation' ,
179+ data : {
180+ id : 101 ,
181+ tool : 'task' ,
182+ system : 'payload' ,
183+ fetchLatest : false ,
184+ taskData : chatCardTestData ,
185+ } ,
186+ } ,
187+ } ,
188+ {
189+ id : '7' ,
190+ role : 'user' ,
191+ message : { type : 'text' , data : { content : 'Thank you! Can I also get help with my printer?' } } ,
192+ } ,
193+ {
194+ id : '8' ,
195+ role : 'assistant' ,
196+ message : { type : 'text' , data : { content : '' } } ,
197+ } ,
198+ {
199+ id : '9' ,
200+ role : 'user' ,
201+ message : { type : 'text' , data : { content : 'It is not connecting to the network.' } } ,
202+ } ,
203+ {
204+ id : '10' ,
205+ role : 'assistant' ,
206+ message : { type : 'forwarding' , data : { content : 'Printer Support' } } ,
207+ } ,
208+ {
209+ id : '11' ,
210+ role : 'assistant' ,
211+ message : { type : 'text' , data : { content : '' } } ,
212+ } ,
213+ {
214+ id : '12' ,
215+ role : 'assistant' ,
216+ message : {
217+ type : 'tool-invocation' ,
218+ data : {
219+ id : 102 ,
220+ tool : 'task' ,
221+ system : 'payload' ,
222+ fetchLatest : false ,
223+ taskData : chatCardTestData ,
224+ } ,
225+ } ,
226+ } ,
227+ {
228+ id : '13' ,
229+ role : 'user' ,
230+ message : { type : 'text' , data : { content : 'How do I check the status of my tickets?' } } ,
231+ } ,
232+ {
233+ id : '14' ,
234+ role : 'assistant' ,
235+ message : { type : 'text' , data : { content : '' } } ,
236+ } ,
237+ {
238+ id : '15' ,
239+ role : 'assistant' ,
240+ message : {
241+ type : 'tool-invocation' ,
242+ data : {
243+ id : 103 ,
244+ tool : 'task' ,
245+ system : 'payload' ,
246+ fetchLatest : false ,
247+ taskData : chatCardTestData ,
248+ } ,
249+ } ,
250+ } ,
251+ ]
252+ // Split assistant text messages into word chunks
253+ const assistantTexts = [
254+ 'Hi! I can help you with that. Can you describe the issue?' , // id:2
255+ 'I have created a support ticket for you.' , // id:5
256+ 'Of course! What issue are you facing with the printer?' , // id:8
257+ 'A new ticket has been created for your printer issue.' , // id:11
258+ 'You can view all your open tickets in the Helpdesk portal.' , // id:14
259+ ]
260+ const assistantChunks = assistantTexts . map ( text => text . split ( / ( \s + ) / ) . filter ( Boolean ) )
261+ let idx = 0
262+ let chunkIdx = 0
263+ let msgIdx = 0
264+ let timer : NodeJS . Timeout | null = null
265+ setMessages ( [ convo [ 0 ] ] )
266+ function streamNext ( currentMessages : ChatMessageProps [ ] ) {
267+ if ( idx >= convo . length ) return
268+ // If this is a streaming assistant text message
269+ if ( [ 1 , 4 , 7 , 10 , 13 ] . includes ( idx ) ) {
270+ // Add the message with empty content if not already present
271+ if ( currentMessages . length < idx + 1 ) {
272+ setMessages ( ( prev ) => {
273+ const next = [ ...prev , convo [ idx ] ]
274+ timer = setTimeout ( ( ) => streamNext ( next ) , 1000 )
275+ return next
276+ } )
277+ return
278+ }
279+ // Stream word chunks for this message
280+ if ( chunkIdx < assistantChunks [ msgIdx ] . length ) {
281+ setMessages ( ( prev ) => {
282+ const updated = [ ...prev ]
283+ const last = { ...updated [ idx ] }
284+ last . message = {
285+ ...last . message ,
286+ data : {
287+ ...last . message . data ,
288+ content : ( last . message . data ?. content || '' ) + assistantChunks [ msgIdx ] [ chunkIdx ] ,
289+ } ,
290+ }
291+ updated [ idx ] = last
292+ timer = setTimeout ( ( ) => streamNext ( updated ) , 200 )
293+ return updated
294+ } )
295+ chunkIdx ++
296+ return
297+ } else {
298+ chunkIdx = 0
299+ msgIdx ++
300+ idx ++
301+ timer = setTimeout ( ( ) => streamNext ( messages ) , 1000 )
302+ return
303+ }
304+ }
305+ // Add the next message
306+ setMessages ( ( prev ) => {
307+ const next = [ ...prev , convo [ idx ] ]
308+ timer = setTimeout ( ( ) => streamNext ( next ) , 1000 )
309+ return next
310+ } )
311+ idx ++
312+ }
313+ timer = setTimeout ( ( ) => streamNext ( [ convo [ 0 ] ] ) , 1000 )
314+ return ( ) => { if ( timer ) clearTimeout ( timer ) }
315+ } , [ ] )
316+ return < ChatInterfaceMessages { ...args } messages = { messages } />
317+ } ,
318+ args : {
319+ contextType : 'project' ,
320+ currentUser : {
321+ id : 1 ,
322+ name : 'John Doe' ,
323+ email : 'john.doe@example.com' ,
324+ avatar : '/path-to-avatar.jpg' ,
325+ role : 'user' ,
326+ } ,
327+ } ,
328+ }
0 commit comments