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