Public
Edited
Nov 3, 2023
Insert cell
Insert cell
Inputs.textarea({
value: JSON.stringify(messages),
})
Insert cell
messages
Insert cell
viewof messages = viewroutine(async function*() {
const root = htl.html`<div>`;
const shadowRoot = root.attachShadow({ mode: 'closed' });
shadowRoot.appendChild(htl.html`<style>${style.replaceAll(':root', ':host')}`)
const self = htl.html`<span>`;
shadowRoot.appendChild(self);

const ui = {
disabled: false,
messages: [
// { role: 'user', content: 'What is the capital of France?' },
// { role: 'assistant', content: 'The capital of France is Paris.' },
],
};

invalidation.then(() => reject());

let resolve, reject;
for (;;) {
ui.disabled = false;
yield (render(), view());
yield new Event('input', { bubbles: true });
const content = await new Promise((res, rej) => {
console.log('new promise');
resolve = res;
reject = rej;
});

ui.disabled = true;
ui.messages.push({
role: 'user',
content,
});
yield (render(), view());
yield new Event('input', { bubbles: true });

const request = {
url: `https://completion.is.mediocreatbest.xyz/samantha/v1/chat/completions`,
method: 'POST',
headers: {
'Content-Type': 'application/json',
// 'Accept': 'application/json',
},
credentials: 'include',
// mode: 'cors',
body: JSON.stringify({
messages: ui.messages,
max_tokens: 2048,
temperature: 0.7,
stream: true,
}),
};

const messages = Generators.queue((onmessage) => {
fetchEventSource(request.url, {
...request,
openWhenHidden: true,
onmessage: ({ data }) => {
// console.log('onmessage', data);
if (data === '') return; // ping
if (data === '[DONE]') return onmessage(null); // done
onmessage(JSON.parse(data));
},
onerror: (err) => {
throw err;
},
});
});

let completion;
ui.messages.push(completion = {
role: 'assistant',
content: '…',
});
yield (render(), view());

let currentCompletion = '';
for await (const message of messages) {
if (message === null) break; // done
const text = message.choices[0].delta.content;
if (text === undefined) continue;
currentCompletion += text;
completion.content = `${currentCompletion}…`;
yield (render(), view());
}
completion.content = `${currentCompletion}`;
yield (render(), view());
}

function view() {
return Object.assign(root, {
value: ui.messages,
});
}

function onsubmit(e) {
console.log('onsubmit');
e.preventDefault();
const form = e.target;
const $content = form.elements.namedItem('content');
const content = $content.value;
$content.value = '';
resolve(content);
}

function $Message({ role, content }) {
if (role === 'user') {
return $UserMessage({ content });
} else if (role === 'assistant') {
return $AssistantMessage({ content });
} else {
throw `bad role: ${role}`;
}
}

function $UserMessage({ content }) {
return htl.html.fragment`
<div class="msg right-msg">
<div
class="msg-img"
style="background-image: url(https://image.flaticon.com/icons/svg/145/145867.svg)"
></div>
<div class="msg-bubble">
<div class="msg-info">
<div class="msg-info-name">User</div>
<!-- <div class="msg-info-time">12:46</div> -->
</div>
<div class="msg-text" style="white-space: pre-line">${content}</div>
</div>
</div>
`/* htl.html.fragment */;
}

function $AssistantMessage({ content }) {
return htl.html.fragment`
<div class="msg left-msg">
<div
class="msg-img"
style="background-image: url(https://image.flaticon.com/icons/svg/327/327779.svg)"
></div>
<div class="msg-bubble">
<div class="msg-info">
<div class="msg-info-name">Assistant</div>
<!-- <div class="msg-info-time">12:45</div> -->
</div>
<div class="msg-text" style="white-space: pre-line">${content}</div>
</div>
</div>
`/* htl.html.fragment */;
}

function render({ disabled, messages }=ui) {
reconcile(self, htl.html`
<section class="msger">
<header class="msger-header">
<div class="msger-header-title">
SimpleChat
</div>
</header>
<main class="msger-chat">
${messages.map($Message)}
</main>
<form class="msger-inputarea" onsubmit=${onsubmit}>
<input autocomplete="off" disabled=${disabled} name="content" type="text" class="msger-input" placeholder="Enter your message...">
<button type="submit" class="msger-send-btn">Send</button>
</form>
</section>
`/* htl.html */);
} /* function render */
})
Insert cell
Insert cell
Insert cell
import {viewroutine, ask, fetchEventSource} from '@player1537/utilities'
Insert cell
import {reconcile} from '@tomlarkworthy/reconcile'
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more