1
+ %% Process Generated Text in Real Time by Using ChatGPT in Streaming Mode
2
+ % This example shows how to process generated text in real time by using ChatGPT
3
+ % in streaming mode.
4
+ %
5
+ % By default, when you pass a prompt to ChatGPT, it generates a response internally
6
+ % and then outputs it in full at the end. To print out and format generated text
7
+ % as the model is generating it, use the |StreamFun| name-value argument of the
8
+ % |openAIChat| class. The streaming function is a custom function handle that
9
+ % tells the model what to do with the output.
10
+ %
11
+ % The example includes two parts:
12
+ %%
13
+ % * First, define and use a custom streaming function to print out generated
14
+ % text directly as the model generates it.
15
+ % * Then, create an HTML UI Component and define and use a custom streaming
16
+ % function to update the UI Component in real time as the model generates text.
17
+ %%
18
+ % To run this example, you need a valid API key from a paid OpenAI API account.
19
+
20
+ loadenv(" .env" )
21
+ addpath(' ..' )
22
+ %% Print Stream Directly to Screen
23
+ % In this example, the streamed output is printed directly to the screen.
24
+ %
25
+ % Define the function to print the returned tokens.
26
+
27
+ function printToken(token )
28
+ fprintf(" %s" ,token );
29
+ end
30
+ %%
31
+ % Create the chat object with the defined function as a handle.
32
+
33
+ chat = openAIChat(StreamFun = @printToken );
34
+ %%
35
+ % Generate response to a prompt in streaming mode.
36
+
37
+ prompt = " What is Model-Based Design?" ;
38
+ generate(chat , prompt , MaxNumTokens= 500 );
39
+ %% Print Stream to HTML UI Component
40
+ % In this example, the streamed output is printed to the HTML component.
41
+ %
42
+ % Create the HTML UI component.
43
+
44
+ fig = uifigure ;
45
+ h = uihtml(fig ,Position= [50 ,10 ,450 ,400 ]);
46
+ %%
47
+ % Initialize the content of the HTML UI component.
48
+
49
+ resetTable(h );
50
+ %%
51
+ % Create the chat object with the function handle, which requires the |uihtml|
52
+ % object created earlier.
53
+
54
+ chat = openAIChat(StreamFun = @(x )printStream(h ,x ));
55
+ %%
56
+ % Add the user prompt to the table in the HTML UI component.
57
+
58
+ userPrompt = " Tell me 5 jokes." ;
59
+ addChat(h ," user" ,userPrompt ," new" )
60
+ %%
61
+ % Generate response to a prompt in streaming mode.
62
+
63
+ [txt , message , response ] = generate(chat ,userPrompt );
64
+ %%
65
+ % Update the last row with the final output. This is necessary if further update
66
+ % is needed to support additional HTML formatting.
67
+
68
+ addChat(h ," assistant" ,txt ," current" )
69
+ %% Helper functions
70
+ % |resetTable|:
71
+ %%
72
+ % # Adds the basic HTML structure and the JavaScript that process the data change
73
+ % in MATLAB.
74
+ % # The JavaScript gets a reference to the table and changed data and if the
75
+ % 3rd element in the data is "new", adds a new row.
76
+ % # It populates the new row with two cells and update the cells from the first
77
+ % two elements of the data.
78
+ % # The new row is then appended to the table.
79
+ % # Otherwise, the JavaScript gets reference to the last cell of the last row
80
+ % of the table, and update it with the 2nd element of the data.
81
+
82
+ function resetTable(obj )
83
+ % RESETTABLE initialize the HTML UI component in the input argument.
84
+ mustBeA(obj ,' matlab.ui.control.HTML' )
85
+ obj.HTMLSource = [' <html><body><table>' ...
86
+ ' <tr><th>Role</th><th>Content</th></tr></table><script>' , ...
87
+ ' function setup(htmlComponent) {' , ...
88
+ ' htmlComponent.addEventListener("DataChanged", function(event) {' , ...
89
+ ' var table = document.querySelector("table");' ...
90
+ ' var changedData = htmlComponent.Data;' , ...
91
+ ' if (changedData[2] == "new") {' , ...
92
+ ' var newRow = document.createElement("tr");' , ...
93
+ ' var cell1 = document.createElement("td");' , ...
94
+ ' var cell2 = document.createElement("td");' , ...
95
+ ' cell1.innerHTML = changedData[0];' , ...
96
+ ' cell2.innerHTML = changedData[1];' , ...
97
+ ' newRow.appendChild(cell1);' , ...
98
+ ' newRow.appendChild(cell2);' , ...
99
+ ' table.appendChild(newRow);' , ...
100
+ ' } else { ' , ...
101
+ ' var lastRow = table.rows[table.rows.length - 1];' , ...
102
+ ' var lastCell = lastRow.cells[lastRow.cells.length - 1];' , ...
103
+ ' lastCell.innerHTML = changedData[1];' , ...
104
+ ' }});}</script></body></html>' ];
105
+ obj.Data = [];
106
+ drawnow
107
+ end
108
+ %%
109
+ % |addRow| adds a new row to the table in the HTML UI component
110
+
111
+ function addChat(obj ,role ,content ,row )
112
+ % ADDCHAT adds a new row or updates the last row of the table
113
+ mustBeA(obj ,' matlab.ui.control.HTML' )
114
+ content = replace(content ,newline ," <br>" );
115
+ obj.Data = {role ,content ,row };
116
+ drawnow
117
+ end
118
+ %%
119
+ % |printStream| is the streaming function and prints the stream in the table
120
+ % in the HTML UI component
121
+
122
+ function printStream(h ,x )
123
+ % PRINTSTREAM prints the stream in a new row in the table
124
+ if strlength(x ) == 0
125
+ % if the first token is 0 length, add a new row
126
+ tokens = string(x );
127
+ h.Data = {" assistant" ,tokens ," new" };
128
+ else
129
+ % otherwise append the new token to the previous tokens
130
+ % if the new token contains a line break, replace
131
+ % it with <br>
132
+ if contains(x ,newline )
133
+ x = replace(x ,newline ," <br>" );
134
+ end
135
+ tokens = h.Data{2 } + string(x );
136
+ % update the existing row.
137
+ h.Data = {" assistant" ,tokens ," current" };
138
+ end
139
+ drawnow
140
+ end
141
+ %%
142
+ % _Copyright 2024 The MathWorks, Inc._
0 commit comments