Skip to content

Commit

Permalink
ability to simulate a stream for html
Browse files Browse the repository at this point in the history
  • Loading branch information
OvidijusParsiunas committed Apr 29, 2024
1 parent e15e102 commit da96666
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 8 deletions.
23 changes: 15 additions & 8 deletions component/src/utils/HTTP/stream.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {EventSourceMessage, fetchEventSource} from '@microsoft/fetch-event-source';
import {MessageStream} from '../../views/chat/messages/stream/messageStream';
import {ServiceIO, StreamHandlers} from '../../services/serviceIO';
import {HTMLUtils} from '../../views/chat/messages/html/htmlUtils';
import {Messages} from '../../views/chat/messages/messages';
import {Response as ResponseI} from '../../types/response';
import {Stream as StreamI} from '../../types/stream';
Expand Down Expand Up @@ -91,21 +92,27 @@ export class Stream {

public static simulate(messages: Messages, sh: StreamHandlers, result: ResponseI) {
const simulationSH = sh as unknown as SimulationSH;
// reason for not streaming html is because there is no standard way to split it
if (result.files || result.html) messages.addNewMessage({sendUpdate: false, ignoreText: true, ...result}, false);
if (result.files) messages.addNewMessage({sendUpdate: false, ignoreText: true, ...result}, false);
if (result.text) {
sh.onOpen();
const responseText = result.text.split(''); // important to split by char for Chinese characters
Stream.populateMessages(responseText, new MessageStream(messages), simulationSH);
const responseTextStrings = result.text.split(''); // important to split by char for Chinese characters
Stream.populateMessages(responseTextStrings, new MessageStream(messages), simulationSH, 'text');
}
if (result.html) {
sh.onOpen();
const responseHTMLStrings = HTMLUtils.splitHTML(result.html);
Stream.populateMessages(responseHTMLStrings, new MessageStream(messages), simulationSH, 'html');
}
}

private static populateMessages(responseText: string[], stream: MessageStream, sh: SimulationSH, charIndex = 0) {
const character = responseText[charIndex];
// prettier-ignore
private static populateMessages(
responseStrings: string[], stream: MessageStream, sh: SimulationSH, type: 'text'|'html', charIndex = 0) {
const character = responseStrings[charIndex];
if (character) {
stream.upsertStreamedMessage({text: character});
stream.upsertStreamedMessage({[type]: character});
const timeout = setTimeout(() => {
Stream.populateMessages(responseText, stream, sh, charIndex + 1);
Stream.populateMessages(responseStrings, stream, sh, type, charIndex + 1);
}, sh.simulationInterim || 6);
sh.abortStream.abort = () => {
Stream.abort(timeout, stream, sh.onClose);
Expand Down
19 changes: 19 additions & 0 deletions component/src/views/chat/messages/html/htmlUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,23 @@ export class HTMLUtils {
HTMLDeepChatElements.applyDeepChatUtilities(messages, messages.htmlClassUtilities, outmostElement);
HTMLUtils.applyCustomClassUtilities(messages.htmlClassUtilities, outmostElement);
}

private static traverseNodes(node: ChildNode, topLevelElements: string[]) {
if (node.nodeType === Node.ELEMENT_NODE) {
topLevelElements.push((node as HTMLElement).outerHTML);
}
node.childNodes.forEach((childNode) => {
HTMLUtils.traverseNodes(childNode, topLevelElements);
});
}

public static splitHTML(htmlString: string) {
const parser = new DOMParser();
const doc = parser.parseFromString(htmlString, 'text/html');
const topLevelElements: string[] = [];
doc.body.childNodes.forEach((childNode) => {
HTMLUtils.traverseNodes(childNode, topLevelElements);
});
return topLevelElements;
}
}

0 comments on commit da96666

Please sign in to comment.