Skip to content

Commit

Permalink
supports output in markdown format
Browse files Browse the repository at this point in the history
Signed-off-by: cbh778899 <[email protected]>
  • Loading branch information
cbh778899 committed Jul 16, 2024
1 parent f74cda8 commit cbb29d1
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 36 deletions.
74 changes: 43 additions & 31 deletions components/chat-page/chatMain.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import useConversation from "../../global/useConversation.js";
import useHistory from "../../global/useHistory.js";
import useModelSettings from "../../global/useModelSettings.js";
import { formatJSON } from "../../tools/conversationFormat.js";
import { formatJSON, formatMarkdown } from "../../tools/conversationFormat.js";
import showMessage from "../../tools/message.js";
import request from "../../tools/request.js";
import getSVG from "../../tools/svgs.js";
Expand Down Expand Up @@ -168,7 +168,7 @@ async function sendMessage(message, send) {
top: main_elem.scrollHeight,
behavior: 'smooth'
})
const [bot_answer, updateMessage] = createBlock('assistant');
const [bot_answer, elements] = createBlock('assistant');
main_elem.appendChild(bot_answer);

let content = ''
Expand All @@ -183,15 +183,12 @@ async function sendMessage(message, send) {
}
}, true)

await send(response, msg=>{
content = msg;
updateMessage(msg);
});
await send(response, elements, c=>content = c);
} catch(error) {
error;
if(content) content+=' ...'
content += '(Message Abroted)'
updateMessage(content)
elements.main = content;
} finally {
appendConversationMessage([
{ role: 'user', message },
Expand All @@ -210,28 +207,43 @@ function sendMessageWaiting(msg) {
}

async function sendMessageStream(msg) {
return sendMessage(msg, async (response, updateMessage) => {
let resp_content = ''
return sendMessage(msg, async (response, elements, updateContent) => {
const { main, pending, started } = elements;
let msg_started = false;
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
let pending_content = ''
let resp_content = '', pending_content = '', html_content = '', end_block = null
while(true) {
const {value, done} = await reader.read();
if(done) break;
pending_content += value;
if(pending_content.includes('\n\n')) {
const splitted_content = pending_content.split('\n\n')
if(pending_content.includes('}\n\n')) {
const splitted_content = pending_content.split('}\n\n')
try {
const json = JSON.parse(splitted_content.shift().replace('data: ', ''))
if(!msg_started) {
msg_started = true;
started();
}
const json = JSON.parse(splitted_content.shift().replace('data: ', '')+'}')
pending_content = splitted_content.join('}\n\n')
resp_content += json.content;
updateMessage(resp_content);
pending_content = splitted_content.join('')
html_content += json.content;
const parsed_md = formatMarkdown(html_content, main, pending, end_block);
main_elem.scrollTo({
top: main_elem.scrollHeight,
behavior: 'smooth'
})
if(parsed_md) {
html_content = parsed_md[0];
end_block = parsed_md[1];
}
updateContent(resp_content);
if(json.stop) break;
} catch(error) {
} catch(error) {
console.error(error);
}
}
}
return resp_content;
formatMarkdown(html_content, main, pending, end_block, true);
})
}

Expand Down Expand Up @@ -262,28 +274,28 @@ function createBlock(role, msg = '') {
block.appendChild(message);

if(role === 'assistant') {
message.innerHTML = `
${getSVG('circle-fill', 'dot-animation dot-1')}
${getSVG('circle-fill', 'dot-animation dot-2')}
${getSVG('circle-fill', 'dot-animation dot-3')}`

block.insertAdjacentHTML("afterbegin", `<img class='avatar' src='/medias/SkywardAI.png'>`)
if(!msg) {
message.innerHTML = `
${getSVG('circle-fill', 'dot-animation dot-1')}
${getSVG('circle-fill', 'dot-animation dot-2')}
${getSVG('circle-fill', 'dot-animation dot-3')}`
}
}

const pending_elem = document.createElement('div');

if(msg) {
message.textContent = msg;
message.appendChild(pending_elem);
formatMarkdown(msg, message, pending_elem, null, true);
}

return [
block,
(msg) => {
if(msg) {
message.textContent = msg;
main_elem.scrollTo({
top: main_elem.scrollHeight,
behavior: 'smooth'
})
}
{
main: message,
pending: pending_elem,
started: ()=>{ message.innerHTML = ''; message.appendChild(pending_elem); }
}
]
}
14 changes: 14 additions & 0 deletions styles/chat_page.css
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,20 @@
animation-delay: .6s;
}

.message .code-block {
background-color: #1f1f1f;
color: white;
padding: 20px 15px;
border-radius: 7px;
margin-top: 5px;
}

.message .inline-code {
background-color: var(--gray-bg);
padding: 3px 7px;
border-radius: 6px;
}

#chat-page #chat-main #submit-chat {
position: absolute;
width: calc(100% - var(--conversation-main-side-padding));
Expand Down
76 changes: 71 additions & 5 deletions tools/conversationFormat.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,74 @@
// export function formatMarkdown(input) {
// input.split('\n').map(line=>{

// })
// }
export function formatMarkdown(str, target_elem, pending_elem, end_special_block = null, force = false) {
if(!str.includes('\n') && !force) {
pending_elem.textContent = str;
return null;
}
const whole_lines = str.split('\n');
let content_left = ''
if(!force) content_left = whole_lines.pop();
pending_elem.textContent = content_left;

function parseSingleLine(pattern_name) {
return (_, group_1, group_2) => {
switch(pattern_name) {
case 'header':
return `<h${group_1.length}>${group_2}</h${group_1.length}>`;
case 'bold': return `<strong>${group_1}</strong>`;
case 'italic': return `<em>${group_1}</em>`;
case 'bold-italic': return `<em><strong>${group_1}</strong></em>`;
case 'hr': return '</hr>';
case 'inline-code': return `<span class="inline-code">${group_2||group_1}</span>`;
}
}
}

function parseLine(line) {
// test if this line is start/end of a code block
const match_code = line.match(/`{3,}/)
if(match_code) {
if(end_special_block) {
if(match_code[0] === end_special_block) {
end_special_block = null;
target_elem.appendChild(pending_elem)
return;
}
} else{
const pattern = match_code[0]
const elem = document.createElement('div')
elem.className = 'code-block';
target_elem.appendChild(elem);

end_special_block = pattern;
elem.appendChild(pending_elem);
return;
}
}

// replace white spaces, no need for single white space
line.replaceAll(' ', '&nbsp;&nbsp;');
if(end_special_block) {
pending_elem.insertAdjacentHTML("beforebegin",line||"</br>");
} else {
const parsed_line = !line ? "</br>" : line
.replace(/(#{6}|#{5}|#{4}|#{3}|#{2}|#{1}) (.*$)/, parseSingleLine('header'))
.replaceAll(/[*_]{3,}(.+?)[*_]{3,}/g, parseSingleLine('bold-italic'))
.replaceAll(/\*\*(.+?)\*\*/g, parseSingleLine('bold'))
.replaceAll(/__(.+?)__/g, parseSingleLine('italic'))
.replaceAll(/^(\*|-){3,}$/g, parseSingleLine('hr'))
.replaceAll(/``(.+?)``|`(.+?)`/g, parseSingleLine('inline-code'))

pending_elem.insertAdjacentHTML("beforebegin", parsed_line);
}
}

whole_lines.forEach(line=>{
parseLine(line)
})

force && pending_elem.remove();

return [content_left, end_special_block]
}

export function formatJSON(conversation, {createdAt, name}) {
const json =
Expand Down

0 comments on commit cbb29d1

Please sign in to comment.