open-webui/src/lib/components/chat/MessageInput/Models.svelte

159 lines
3.7 KiB
Svelte
Raw Normal View History

2024-01-10 06:47:31 +00:00
<script lang="ts">
import { generatePrompt } from '$lib/apis/ollama';
import { models } from '$lib/stores';
import { splitStream } from '$lib/utils';
import { tick } from 'svelte';
import toast from 'svelte-french-toast';
export let prompt = '';
export let user = null;
export let chatInputPlaceholder = '';
export let messages = [];
let selectedIdx = 0;
let filteredModels = [];
$: filteredModels = $models
2024-01-10 06:56:43 +00:00
.filter(
(p) =>
p.name !== 'hr' &&
!p.external &&
p.name.includes(prompt.split(' ')?.at(0)?.substring(1) ?? '')
)
2024-01-10 06:47:31 +00:00
.sort((a, b) => a.name.localeCompare(b.name));
$: if (prompt) {
selectedIdx = 0;
}
export const selectUp = () => {
selectedIdx = Math.max(0, selectedIdx - 1);
};
export const selectDown = () => {
selectedIdx = Math.min(selectedIdx + 1, filteredModels.length - 1);
};
const confirmSelect = async (model) => {
// dispatch('select', model);
prompt = '';
user = JSON.parse(JSON.stringify(model.name));
await tick();
chatInputPlaceholder = `'${model.name}' is thinking...`;
const chatInputElement = document.getElementById('chat-textarea');
await tick();
chatInputElement?.focus();
await tick();
const convoText = messages.reduce((a, message, i, arr) => {
return `${a}### ${message.role.toUpperCase()}\n${message.content}\n\n`;
}, '');
const res = await generatePrompt(localStorage.token, model.name, convoText);
if (res && res.ok) {
const reader = res.body
.pipeThrough(new TextDecoderStream())
.pipeThrough(splitStream('\n'))
.getReader();
while (true) {
const { value, done } = await reader.read();
if (done) {
break;
}
try {
let lines = value.split('\n');
for (const line of lines) {
if (line !== '') {
console.log(line);
let data = JSON.parse(line);
if ('detail' in data) {
throw data;
}
if (data.done == false) {
if (prompt == '' && data.response == '\n') {
continue;
} else {
prompt += data.response;
console.log(data.response);
chatInputElement.scrollTop = chatInputElement.scrollHeight;
await tick();
}
}
}
}
} catch (error) {
console.log(error);
if ('detail' in error) {
toast.error(error.detail);
}
break;
}
}
} else {
if (res !== null) {
const error = await res.json();
console.log(error);
if ('detail' in error) {
toast.error(error.detail);
} else {
toast.error(error.error);
}
} else {
toast.error(`Uh-oh! There was an issue connecting to Ollama.`);
}
}
chatInputPlaceholder = '';
console.log(user);
};
</script>
{#if filteredModels.length > 0}
2024-02-16 00:20:46 +00:00
<div class="md:px-2 mb-3 text-left w-full absolute bottom-0 left-0 right-0">
2024-02-25 21:27:19 +00:00
<div class="flex w-full px-2">
<div class=" bg-gray-100 dark:bg-gray-700 w-10 rounded-l-xl text-center">
2024-01-10 06:47:31 +00:00
<div class=" text-lg font-semibold mt-2">@</div>
</div>
2024-02-25 21:27:19 +00:00
<div class="max-h-60 flex flex-col w-full rounded-r-xl bg-white">
<div class="m-1 overflow-y-auto p-1 rounded-r-xl space-y-0.5">
2024-01-10 06:47:31 +00:00
{#each filteredModels as model, modelIdx}
<button
2024-02-25 21:27:19 +00:00
class=" px-3 py-1.5 rounded-xl w-full text-left {modelIdx === selectedIdx
2024-01-10 06:47:31 +00:00
? ' bg-gray-100 selected-command-option-button'
: ''}"
type="button"
on:click={() => {
confirmSelect(model);
}}
on:mousemove={() => {
selectedIdx = modelIdx;
}}
on:focus={() => {}}
>
<div class=" font-medium text-black line-clamp-1">
{model.name}
</div>
<!-- <div class=" text-xs text-gray-600 line-clamp-1">
{doc.title}
</div> -->
</button>
{/each}
</div>
</div>
</div>
</div>
{/if}