const md = window.markdownit({ linkify: true, highlight(code, lang) { const language = hljs.getLanguage(lang) ? lang : 'plaintext'; const html = hljs.highlight(code, {language: language, ignoreIllegals: true }).value return `
${language}
${html}
` }, }); new ClipboardJS('.hljs-copy-button', { target: function(trigger) { console.log(trigger.parentNode.nextElementSibling) return trigger.parentNode.nextElementSibling; } }); async function getApiUrl() { if (getApiUrl.url) return getApiUrl.url; try { const response = await fetch("/v1/chat/completions", { method: "OPTIONS", }); if (response.status !== 200) throw new Error(); getApiUrl.url = "/v1/chat/completions"; const corsHeaders = ( response.headers.get("Access-Control-Allow-Headers") || "" ).toLowerCase(); getApiUrl.tokenRequired = corsHeaders.includes("authorization"); } catch (e) { getApiUrl.url = "https://api.openai.com/v1/chat/completions"; getApiUrl.tokenRequired = true; } return getApiUrl.url; } async function postRequest(url, headers, body) { const response = await fetch(url, { method: "POST", headers: headers, body: JSON.stringify(body), }); if (!response.ok) { throw new Error(await response.text()); } return response; } async function readStream(stream, progressCallback) { const reader = stream.getReader(); const textDecoder = new TextDecoder('utf-8'); let responseObj = {}; while (true) { const { done, value } = await reader.read(); if (done) break; const lines = textDecoder.decode(value).split("\n"); processLines(lines, responseObj, progressCallback); } return responseObj; } function processLines(lines, responseObj, progressCallback) { for (const line of lines) { if (line.startsWith("data: ")) { if (line.includes("[DONE]")) { return responseObj; } try { const data = JSON.parse(line.slice(6)); const delta = data.choices[0].delta; Object.keys(delta).forEach(key => { responseObj[key] = (responseObj[key] || "") + delta[key]; progressCallback(responseObj); }); } catch (e) { console.log("Error parsing line:", line); } } } } async function complete(messages, token, progressCallback) { const apiUrl = await getApiUrl(); const headers = { "Content-Type": "application/json" }; if (getApiUrl.tokenRequired) { headers.Authorization = `Bearer ${token}`; } const body = { model: "gpt-3.5-turbo", messages: messages, stream: true, }; const response = await postRequest(apiUrl, headers, body); return readStream(response.body, progressCallback); } function chatMessage(message) { return { scrollToBottom() { const chatContainer = document.getElementById('chatContainer'); const main = chatContainer.parentElement; main.scrollTop = main.scrollHeight; }, watchEffect(message) { this.$nextTick(() => { this.$el.innerHTML = md.render(message); this.scrollToBottom() }); } } } function chatApp() { return { messages: [], newMessage: '', init() { this.messages = [ { role: 'system', content: 'You are a programing assistant. Answer using markdown.' } ]; hljs.configure({ 'cssSelector' : 'pre code' }); }, sendMessage() { if (this.newMessage.trim() === '') return; const userMessage = { role: 'user', content: this.newMessage }; this.messages.push(userMessage); this.messages.push({ role: 'assistant', content: '' }); const lastMsgIndex = this.messages.length - 1; try { complete( this.messages, 'no-token', (message) => { if (message.content) this.messages[lastMsgIndex].content = message.content; } ); } catch (error) { console.log(error.message); return; } finally { this.newMessage = ''; } }, clearMessages() { this.messages = []; }, }; }