forked from open-webui/open-webui
Merge pull request #186 from ollama-webui/dev
feat: `/chat` route support
This commit is contained in:
commit
a7bec01d7b
4 changed files with 377 additions and 303 deletions
|
@ -2,6 +2,8 @@
|
|||
|
||||
## Connection Errors
|
||||
|
||||
Make sure you have the **latest version of Ollama** installed before proceeding with the installation. You can find the latest version of Ollama at [https://ollama.ai/](https://ollama.ai/).
|
||||
|
||||
If you encounter difficulties connecting to the Ollama server, please follow these steps to diagnose and resolve the issue:
|
||||
|
||||
**1. Verify Ollama Server Configuration**
|
||||
|
@ -43,5 +45,6 @@ docker run --platform linux/amd64 -d -p 3000:8080 -e OLLAMA_API_BASE_URL=http://
|
|||
```
|
||||
|
||||
## References
|
||||
|
||||
[Change Docker Desktop Settings on Mac](https://docs.docker.com/desktop/settings/mac/) Search for "x86" in that page.
|
||||
[Run x86 (Intel) and ARM based images on Apple Silicon (M1) Macs?](https://forums.docker.com/t/run-x86-intel-and-arm-based-images-on-apple-silicon-m1-macs/117123)
|
||||
|
|
|
@ -162,7 +162,15 @@
|
|||
const editMessageHandler = async (messageId) => {
|
||||
// let editMessage = history.messages[messageId];
|
||||
history.messages[messageId].edit = true;
|
||||
history.messages[messageId].originalContent = history.messages[messageId].content;
|
||||
history.messages[messageId].editedContent = history.messages[messageId].content;
|
||||
|
||||
await tick();
|
||||
|
||||
const editElement = document.getElementById(`message-edit-${messageId}`);
|
||||
|
||||
editElement.style.height = '';
|
||||
editElement.style.height = `${editElement.scrollHeight}px`;
|
||||
};
|
||||
|
||||
const confirmEditMessage = async (messageId) => {
|
||||
|
@ -195,6 +203,11 @@
|
|||
await sendPrompt(userPrompt, userMessageId, $chatId);
|
||||
};
|
||||
|
||||
const confirmEditResponseMessage = async (messageId) => {
|
||||
history.messages[messageId].edit = false;
|
||||
history.messages[messageId].content = history.messages[messageId].editedContent;
|
||||
};
|
||||
|
||||
const cancelEditMessage = (messageId) => {
|
||||
history.messages[messageId].edit = false;
|
||||
history.messages[messageId].editedContent = undefined;
|
||||
|
@ -415,19 +428,15 @@
|
|||
{#if message?.edit === true}
|
||||
<div class=" w-full">
|
||||
<textarea
|
||||
id="message-edit-{message.id}"
|
||||
class=" bg-transparent outline-none w-full resize-none"
|
||||
bind:value={history.messages[message.id].editedContent}
|
||||
on:input={(e) => {
|
||||
e.target.style.height = '';
|
||||
e.target.style.height = `${e.target.scrollHeight}px`;
|
||||
}}
|
||||
on:focus={(e) => {
|
||||
e.target.style.height = '';
|
||||
e.target.style.height = `${e.target.scrollHeight}px`;
|
||||
}}
|
||||
/>
|
||||
|
||||
<div class=" mt-2 flex justify-center space-x-2 text-sm font-medium">
|
||||
<div class=" mt-2 mb-1 flex justify-center space-x-2 text-sm font-medium">
|
||||
<button
|
||||
class="px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded-lg"
|
||||
on:click={() => {
|
||||
|
@ -609,6 +618,41 @@
|
|||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{#if message.role === 'assistant'}
|
||||
<div>
|
||||
{#if message?.edit === true}
|
||||
<div class=" w-full">
|
||||
<textarea
|
||||
id="message-edit-{message.id}"
|
||||
class=" bg-transparent outline-none w-full resize-none"
|
||||
bind:value={history.messages[message.id].editedContent}
|
||||
on:input={(e) => {
|
||||
e.target.style.height = `${e.target.scrollHeight}px`;
|
||||
}}
|
||||
/>
|
||||
|
||||
<div class=" mt-2 mb-1 flex justify-center space-x-2 text-sm font-medium">
|
||||
<button
|
||||
class="px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded-lg"
|
||||
on:click={() => {
|
||||
confirmEditResponseMessage(message.id);
|
||||
}}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
|
||||
<button
|
||||
class=" px-4 py-2 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-100 transition outline outline-1 outline-gray-200 dark:outline-gray-600 rounded-lg"
|
||||
on:click={() => {
|
||||
cancelEditMessage(message.id);
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="w-full">
|
||||
{@html marked(message.content.replace('\\\\', '\\\\\\'))}
|
||||
|
@ -638,8 +682,9 @@
|
|||
</button>
|
||||
|
||||
<div class="text-xs font-bold self-center">
|
||||
{history.messages[message.parentId].childrenIds.indexOf(message.id) +
|
||||
1} / {history.messages[message.parentId].childrenIds.length}
|
||||
{history.messages[message.parentId].childrenIds.indexOf(
|
||||
message.id
|
||||
) + 1} / {history.messages[message.parentId].childrenIds.length}
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
@ -664,8 +709,10 @@
|
|||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- <button
|
||||
class="invisible group-hover:visible p-1 rounded dark:hover:bg-gray-800 transition"
|
||||
<button
|
||||
class="{messageIdx + 1 === messages.length
|
||||
? 'visible'
|
||||
: 'invisible group-hover:visible'} p-1 rounded dark:hover:bg-gray-800 transition"
|
||||
on:click={() => {
|
||||
editMessageHandler(message.id);
|
||||
}}
|
||||
|
@ -684,7 +731,7 @@
|
|||
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
|
||||
/>
|
||||
</svg>
|
||||
</button> -->
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="{messageIdx + 1 === messages.length
|
||||
|
@ -810,6 +857,8 @@
|
|||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<!-- {} -->
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -76,6 +76,13 @@
|
|||
selectedModels = $page.url.searchParams.get('models')
|
||||
? $page.url.searchParams.get('models')?.split(',')
|
||||
: $settings.models ?? [''];
|
||||
|
||||
let _settings = JSON.parse(localStorage.getItem('settings') ?? '{}');
|
||||
console.log(_settings);
|
||||
settings.set({
|
||||
...$settings,
|
||||
..._settings
|
||||
});
|
||||
};
|
||||
|
||||
//////////////////////////
|
||||
|
@ -118,38 +125,11 @@
|
|||
];
|
||||
}
|
||||
|
||||
await tick();
|
||||
|
||||
window.scrollTo({ top: document.body.scrollHeight });
|
||||
|
||||
const res = await fetch(`${$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL}/generate`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'text/event-stream',
|
||||
...($settings.authHeader && { Authorization: $settings.authHeader }),
|
||||
...($user && { Authorization: `Bearer ${localStorage.token}` })
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: model,
|
||||
prompt: userPrompt,
|
||||
system: $settings.system ?? undefined,
|
||||
options: {
|
||||
seed: $settings.seed ?? undefined,
|
||||
temperature: $settings.temperature ?? undefined,
|
||||
repeat_penalty: $settings.repeat_penalty ?? undefined,
|
||||
top_k: $settings.top_k ?? undefined,
|
||||
top_p: $settings.top_p ?? undefined,
|
||||
num_ctx: $settings.num_ctx ?? undefined,
|
||||
...($settings.options ?? {})
|
||||
},
|
||||
format: $settings.requestFormat ?? undefined,
|
||||
context:
|
||||
history.messages[parentId] !== null &&
|
||||
history.messages[parentId].parentId in history.messages
|
||||
? history.messages[history.messages[parentId].parentId]?.context ?? undefined
|
||||
: undefined
|
||||
})
|
||||
});
|
||||
|
||||
// const res = await fetch(`${$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL}/chat`, {
|
||||
// const res = await fetch(`${$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL}/generate`, {
|
||||
// method: 'POST',
|
||||
// headers: {
|
||||
// 'Content-Type': 'text/event-stream',
|
||||
|
@ -158,17 +138,8 @@
|
|||
// },
|
||||
// body: JSON.stringify({
|
||||
// model: model,
|
||||
// messages: [
|
||||
// $settings.system
|
||||
// ? {
|
||||
// role: 'system',
|
||||
// content: $settings.system
|
||||
// }
|
||||
// : undefined,
|
||||
// ...messages
|
||||
// ]
|
||||
// .filter((message) => message)
|
||||
// .map((message) => ({ role: message.role, content: message.content })),
|
||||
// prompt: userPrompt,
|
||||
// system: $settings.system ?? undefined,
|
||||
// options: {
|
||||
// seed: $settings.seed ?? undefined,
|
||||
// temperature: $settings.temperature ?? undefined,
|
||||
|
@ -178,10 +149,48 @@
|
|||
// num_ctx: $settings.num_ctx ?? undefined,
|
||||
// ...($settings.options ?? {})
|
||||
// },
|
||||
// format: $settings.requestFormat ?? undefined
|
||||
// format: $settings.requestFormat ?? undefined,
|
||||
// context:
|
||||
// history.messages[parentId] !== null &&
|
||||
// history.messages[parentId].parentId in history.messages
|
||||
// ? history.messages[history.messages[parentId].parentId]?.context ?? undefined
|
||||
// : undefined
|
||||
// })
|
||||
// });
|
||||
|
||||
const res = await fetch(`${$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL}/chat`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'text/event-stream',
|
||||
...($settings.authHeader && { Authorization: $settings.authHeader }),
|
||||
...($user && { Authorization: `Bearer ${localStorage.token}` })
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: model,
|
||||
messages: [
|
||||
$settings.system
|
||||
? {
|
||||
role: 'system',
|
||||
content: $settings.system
|
||||
}
|
||||
: undefined,
|
||||
...messages
|
||||
]
|
||||
.filter((message) => message)
|
||||
.map((message) => ({ role: message.role, content: message.content })),
|
||||
options: {
|
||||
seed: $settings.seed ?? undefined,
|
||||
temperature: $settings.temperature ?? undefined,
|
||||
repeat_penalty: $settings.repeat_penalty ?? undefined,
|
||||
top_k: $settings.top_k ?? undefined,
|
||||
top_p: $settings.top_p ?? undefined,
|
||||
num_ctx: $settings.num_ctx ?? undefined,
|
||||
...($settings.options ?? {})
|
||||
},
|
||||
format: $settings.requestFormat ?? undefined
|
||||
})
|
||||
});
|
||||
|
||||
const reader = res.body
|
||||
.pipeThrough(new TextDecoderStream())
|
||||
.pipeThrough(splitStream('\n'))
|
||||
|
@ -189,12 +198,9 @@
|
|||
|
||||
while (true) {
|
||||
const { value, done } = await reader.read();
|
||||
if (done || stopResponseFlag) {
|
||||
if (stopResponseFlag) {
|
||||
if (done || stopResponseFlag || _chatId !== $chatId) {
|
||||
responseMessage.done = true;
|
||||
messages = messages;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -205,18 +211,28 @@
|
|||
if (line !== '') {
|
||||
console.log(line);
|
||||
let data = JSON.parse(line);
|
||||
|
||||
if ('detail' in data) {
|
||||
throw data;
|
||||
}
|
||||
|
||||
if (data.done == false) {
|
||||
if (responseMessage.content == '' && data.response == '\n') {
|
||||
if (responseMessage.content == '' && data.message.content == '\n') {
|
||||
continue;
|
||||
} else {
|
||||
responseMessage.content += data.response;
|
||||
responseMessage.content += data.message.content;
|
||||
messages = messages;
|
||||
}
|
||||
} else if ('detail' in data) {
|
||||
throw data;
|
||||
} else {
|
||||
responseMessage.done = true;
|
||||
responseMessage.context = data.context;
|
||||
responseMessage.context = data.context ?? null;
|
||||
responseMessage.info = {
|
||||
total_duration: data.total_duration,
|
||||
prompt_eval_count: data.prompt_eval_count,
|
||||
prompt_eval_duration: data.prompt_eval_duration,
|
||||
eval_count: data.eval_count,
|
||||
eval_duration: data.eval_duration
|
||||
};
|
||||
messages = messages;
|
||||
}
|
||||
}
|
||||
|
@ -324,12 +340,9 @@
|
|||
|
||||
while (true) {
|
||||
const { value, done } = await reader.read();
|
||||
if (done || stopResponseFlag) {
|
||||
if (stopResponseFlag) {
|
||||
if (done || stopResponseFlag || _chatId !== $chatId) {
|
||||
responseMessage.done = true;
|
||||
messages = messages;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -152,38 +152,11 @@
|
|||
];
|
||||
}
|
||||
|
||||
await tick();
|
||||
|
||||
window.scrollTo({ top: document.body.scrollHeight });
|
||||
|
||||
const res = await fetch(`${$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL}/generate`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'text/event-stream',
|
||||
...($settings.authHeader && { Authorization: $settings.authHeader }),
|
||||
...($user && { Authorization: `Bearer ${localStorage.token}` })
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: model,
|
||||
prompt: userPrompt,
|
||||
system: $settings.system ?? undefined,
|
||||
options: {
|
||||
seed: $settings.seed ?? undefined,
|
||||
temperature: $settings.temperature ?? undefined,
|
||||
repeat_penalty: $settings.repeat_penalty ?? undefined,
|
||||
top_k: $settings.top_k ?? undefined,
|
||||
top_p: $settings.top_p ?? undefined,
|
||||
num_ctx: $settings.num_ctx ?? undefined,
|
||||
...($settings.options ?? {})
|
||||
},
|
||||
format: $settings.requestFormat ?? undefined,
|
||||
context:
|
||||
history.messages[parentId] !== null &&
|
||||
history.messages[parentId].parentId in history.messages
|
||||
? history.messages[history.messages[parentId].parentId]?.context ?? undefined
|
||||
: undefined
|
||||
})
|
||||
});
|
||||
|
||||
// const res = await fetch(`${$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL}/chat`, {
|
||||
// const res = await fetch(`${$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL}/generate`, {
|
||||
// method: 'POST',
|
||||
// headers: {
|
||||
// 'Content-Type': 'text/event-stream',
|
||||
|
@ -192,17 +165,8 @@
|
|||
// },
|
||||
// body: JSON.stringify({
|
||||
// model: model,
|
||||
// messages: [
|
||||
// $settings.system
|
||||
// ? {
|
||||
// role: 'system',
|
||||
// content: $settings.system
|
||||
// }
|
||||
// : undefined,
|
||||
// ...messages
|
||||
// ]
|
||||
// .filter((message) => message)
|
||||
// .map((message) => ({ role: message.role, content: message.content })),
|
||||
// prompt: userPrompt,
|
||||
// system: $settings.system ?? undefined,
|
||||
// options: {
|
||||
// seed: $settings.seed ?? undefined,
|
||||
// temperature: $settings.temperature ?? undefined,
|
||||
|
@ -212,10 +176,48 @@
|
|||
// num_ctx: $settings.num_ctx ?? undefined,
|
||||
// ...($settings.options ?? {})
|
||||
// },
|
||||
// format: $settings.requestFormat ?? undefined
|
||||
// format: $settings.requestFormat ?? undefined,
|
||||
// context:
|
||||
// history.messages[parentId] !== null &&
|
||||
// history.messages[parentId].parentId in history.messages
|
||||
// ? history.messages[history.messages[parentId].parentId]?.context ?? undefined
|
||||
// : undefined
|
||||
// })
|
||||
// });
|
||||
|
||||
const res = await fetch(`${$settings?.API_BASE_URL ?? OLLAMA_API_BASE_URL}/chat`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'text/event-stream',
|
||||
...($settings.authHeader && { Authorization: $settings.authHeader }),
|
||||
...($user && { Authorization: `Bearer ${localStorage.token}` })
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: model,
|
||||
messages: [
|
||||
$settings.system
|
||||
? {
|
||||
role: 'system',
|
||||
content: $settings.system
|
||||
}
|
||||
: undefined,
|
||||
...messages
|
||||
]
|
||||
.filter((message) => message)
|
||||
.map((message) => ({ role: message.role, content: message.content })),
|
||||
options: {
|
||||
seed: $settings.seed ?? undefined,
|
||||
temperature: $settings.temperature ?? undefined,
|
||||
repeat_penalty: $settings.repeat_penalty ?? undefined,
|
||||
top_k: $settings.top_k ?? undefined,
|
||||
top_p: $settings.top_p ?? undefined,
|
||||
num_ctx: $settings.num_ctx ?? undefined,
|
||||
...($settings.options ?? {})
|
||||
},
|
||||
format: $settings.requestFormat ?? undefined
|
||||
})
|
||||
});
|
||||
|
||||
const reader = res.body
|
||||
.pipeThrough(new TextDecoderStream())
|
||||
.pipeThrough(splitStream('\n'))
|
||||
|
@ -223,12 +225,9 @@
|
|||
|
||||
while (true) {
|
||||
const { value, done } = await reader.read();
|
||||
if (done || stopResponseFlag) {
|
||||
if (stopResponseFlag) {
|
||||
if (done || stopResponseFlag || _chatId !== $chatId) {
|
||||
responseMessage.done = true;
|
||||
messages = messages;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -239,18 +238,28 @@
|
|||
if (line !== '') {
|
||||
console.log(line);
|
||||
let data = JSON.parse(line);
|
||||
|
||||
if ('detail' in data) {
|
||||
throw data;
|
||||
}
|
||||
|
||||
if (data.done == false) {
|
||||
if (responseMessage.content == '' && data.response == '\n') {
|
||||
if (responseMessage.content == '' && data.message.content == '\n') {
|
||||
continue;
|
||||
} else {
|
||||
responseMessage.content += data.response;
|
||||
responseMessage.content += data.message.content;
|
||||
messages = messages;
|
||||
}
|
||||
} else if ('detail' in data) {
|
||||
throw data;
|
||||
} else {
|
||||
responseMessage.done = true;
|
||||
responseMessage.context = data.context;
|
||||
responseMessage.context = data.context ?? null;
|
||||
responseMessage.info = {
|
||||
total_duration: data.total_duration,
|
||||
prompt_eval_count: data.prompt_eval_count,
|
||||
prompt_eval_duration: data.prompt_eval_duration,
|
||||
eval_count: data.eval_count,
|
||||
eval_duration: data.eval_duration
|
||||
};
|
||||
messages = messages;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue