Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
chenpangpang
open-webui
Commits
68c182db
Unverified
Commit
68c182db
authored
May 07, 2024
by
Timothy Jaeryang Baek
Committed by
GitHub
May 07, 2024
Browse files
Merge branch 'dev' into main
parents
242e70d1
876853f2
Changes
89
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
198 additions
and
133 deletions
+198
-133
kubernetes/helm/values-minikube.yaml
kubernetes/helm/values-minikube.yaml
+0
-27
kubernetes/helm/values.yaml
kubernetes/helm/values.yaml
+0
-75
src/app.css
src/app.css
+9
-0
src/app.html
src/app.html
+6
-0
src/lib/apis/chats/index.ts
src/lib/apis/chats/index.ts
+19
-6
src/lib/apis/ollama/index.ts
src/lib/apis/ollama/index.ts
+6
-2
src/lib/apis/openai/index.ts
src/lib/apis/openai/index.ts
+4
-2
src/lib/apis/rag/index.ts
src/lib/apis/rag/index.ts
+3
-2
src/lib/apis/streaming/index.ts
src/lib/apis/streaming/index.ts
+11
-0
src/lib/components/ChangelogModal.svelte
src/lib/components/ChangelogModal.svelte
+2
-4
src/lib/components/admin/AddUserModal.svelte
src/lib/components/admin/AddUserModal.svelte
+1
-1
src/lib/components/chat/MessageInput/Documents.svelte
src/lib/components/chat/MessageInput/Documents.svelte
+1
-1
src/lib/components/chat/MessageInput/Suggestions.svelte
src/lib/components/chat/MessageInput/Suggestions.svelte
+5
-3
src/lib/components/chat/Messages/CitationsModal.svelte
src/lib/components/chat/Messages/CitationsModal.svelte
+77
-0
src/lib/components/chat/Messages/RateComment.svelte
src/lib/components/chat/Messages/RateComment.svelte
+1
-1
src/lib/components/chat/Messages/ResponseMessage.svelte
src/lib/components/chat/Messages/ResponseMessage.svelte
+45
-1
src/lib/components/chat/ModelSelector.svelte
src/lib/components/chat/ModelSelector.svelte
+1
-1
src/lib/components/chat/ModelSelector/Selector.svelte
src/lib/components/chat/ModelSelector/Selector.svelte
+4
-4
src/lib/components/chat/Settings/Account.svelte
src/lib/components/chat/Settings/Account.svelte
+2
-2
src/lib/components/chat/Settings/Connections.svelte
src/lib/components/chat/Settings/Connections.svelte
+1
-1
No files found.
kubernetes/helm/values-minikube.yaml
deleted
100644 → 0
View file @
242e70d1
ollama
:
resources
:
requests
:
cpu
:
"
2000m"
memory
:
"
2Gi"
limits
:
cpu
:
"
4000m"
memory
:
"
4Gi"
nvidia.com/gpu
:
"
0"
service
:
type
:
ClusterIP
gpu
:
enabled
:
false
webui
:
resources
:
requests
:
cpu
:
"
500m"
memory
:
"
500Mi"
limits
:
cpu
:
"
1000m"
memory
:
"
1Gi"
ingress
:
enabled
:
true
host
:
open-webui.minikube.local
service
:
type
:
NodePort
kubernetes/helm/values.yaml
deleted
100644 → 0
View file @
242e70d1
nameOverride
:
"
"
ollama
:
externalHost
:
"
"
annotations
:
{}
podAnnotations
:
{}
replicaCount
:
1
image
:
repository
:
ollama/ollama
tag
:
latest
pullPolicy
:
Always
resources
:
{}
persistence
:
enabled
:
true
size
:
30Gi
existingClaim
:
"
"
accessModes
:
-
ReadWriteOnce
storageClass
:
"
"
selector
:
{}
annotations
:
{}
nodeSelector
:
{}
# -- If using a special runtime container such as nvidia, set it here.
runtimeClassName
:
"
"
tolerations
:
-
key
:
nvidia.com/gpu
operator
:
Exists
effect
:
NoSchedule
service
:
type
:
ClusterIP
annotations
:
{}
port
:
80
containerPort
:
11434
gpu
:
# -- Enable additional ENV values to help Ollama discover GPU usage
enabled
:
false
webui
:
annotations
:
{}
podAnnotations
:
{}
replicaCount
:
1
image
:
repository
:
ghcr.io/open-webui/open-webui
tag
:
"
"
pullPolicy
:
Always
resources
:
{}
ingress
:
enabled
:
false
class
:
"
"
# -- Use appropriate annotations for your Ingress controller, e.g., for NGINX:
# nginx.ingress.kubernetes.io/rewrite-target: /
annotations
:
{}
host
:
"
"
tls
:
false
existingSecret
:
"
"
persistence
:
enabled
:
true
size
:
2Gi
existingClaim
:
"
"
# -- If using multiple replicas, you must update accessModes to ReadWriteMany
accessModes
:
-
ReadWriteOnce
storageClass
:
"
"
selector
:
{}
annotations
:
{}
nodeSelector
:
{}
tolerations
:
[]
service
:
type
:
ClusterIP
annotations
:
{}
port
:
80
containerPort
:
8080
nodePort
:
"
"
labels
:
{}
loadBalancerClass
:
"
"
src/app.css
View file @
68c182db
...
...
@@ -82,3 +82,12 @@ select {
.katex-mathml
{
display
:
none
;
}
.scrollbar-none
:active::-webkit-scrollbar-thumb
,
.scrollbar-none
:focus::-webkit-scrollbar-thumb
,
.scrollbar-none
:hover::-webkit-scrollbar-thumb
{
visibility
:
visible
;
}
.scrollbar-none
::-webkit-scrollbar-thumb
{
visibility
:
hidden
;
}
src/app.html
View file @
68c182db
...
...
@@ -6,6 +6,12 @@
<link
rel=
"manifest"
href=
"%sveltekit.assets%/manifest.json"
crossorigin=
"use-credentials"
/>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1, maximum-scale=1"
/>
<meta
name=
"robots"
content=
"noindex,nofollow"
/>
<link
rel=
"search"
type=
"application/opensearchdescription+xml"
title=
"Open WebUI"
href=
"/opensearch.xml"
/>
<script>
// On page load or when changing themes, best to add inline in `head` to avoid FOUC
(()
=>
{
...
...
src/lib/apis/chats/index.ts
View file @
68c182db
import
{
WEBUI_API_BASE_URL
}
from
'
$lib/constants
'
;
import
{
getTimeRange
}
from
'
$lib/utils
'
;
export
const
createNewChat
=
async
(
token
:
string
,
chat
:
object
)
=>
{
let
error
=
null
;
...
...
@@ -59,7 +60,10 @@ export const getChatList = async (token: string = '') => {
throw
error
;
}
return
res
;
return
res
.
map
((
chat
)
=>
({
...
chat
,
time_range
:
getTimeRange
(
chat
.
updated_at
)
}));
};
export
const
getChatListByUserId
=
async
(
token
:
string
=
''
,
userId
:
string
)
=>
{
...
...
@@ -90,7 +94,10 @@ export const getChatListByUserId = async (token: string = '', userId: string) =>
throw
error
;
}
return
res
;
return
res
.
map
((
chat
)
=>
({
...
chat
,
time_range
:
getTimeRange
(
chat
.
updated_at
)
}));
};
export
const
getArchivedChatList
=
async
(
token
:
string
=
''
)
=>
{
...
...
@@ -220,13 +227,16 @@ export const getAllChatTags = async (token: string) => {
export
const
getChatListByTagName
=
async
(
token
:
string
=
''
,
tagName
:
string
)
=>
{
let
error
=
null
;
const
res
=
await
fetch
(
`
${
WEBUI_API_BASE_URL
}
/chats/tags
/tag/
${
tagName
}
`
,
{
method
:
'
GE
T
'
,
const
res
=
await
fetch
(
`
${
WEBUI_API_BASE_URL
}
/chats/tags`
,
{
method
:
'
POS
T
'
,
headers
:
{
Accept
:
'
application/json
'
,
'
Content-Type
'
:
'
application/json
'
,
...(
token
&&
{
authorization
:
`Bearer
${
token
}
`
})
}
},
body
:
JSON
.
stringify
({
name
:
tagName
})
})
.
then
(
async
(
res
)
=>
{
if
(
!
res
.
ok
)
throw
await
res
.
json
();
...
...
@@ -245,7 +255,10 @@ export const getChatListByTagName = async (token: string = '', tagName: string)
throw
error
;
}
return
res
;
return
res
.
map
((
chat
)
=>
({
...
chat
,
time_range
:
getTimeRange
(
chat
.
updated_at
)
}));
};
export
const
getChatById
=
async
(
token
:
string
,
id
:
string
)
=>
{
...
...
src/lib/apis/ollama/index.ts
View file @
68c182db
...
...
@@ -159,7 +159,11 @@ export const generateTitle = async (
body
:
JSON
.
stringify
({
model
:
model
,
prompt
:
template
,
stream
:
false
stream
:
false
,
options
:
{
// Restrict the number of tokens generated to 50
num_predict
:
50
}
})
})
.
then
(
async
(
res
)
=>
{
...
...
@@ -178,7 +182,7 @@ export const generateTitle = async (
throw
error
;
}
return
res
?.
response
??
'
New Chat
'
;
return
res
?.
response
.
replace
(
/
[
"'
]
/g
,
''
)
??
'
New Chat
'
;
};
export
const
generatePrompt
=
async
(
token
:
string
=
''
,
model
:
string
,
conversation
:
string
)
=>
{
...
...
src/lib/apis/openai/index.ts
View file @
68c182db
...
...
@@ -295,7 +295,9 @@ export const generateTitle = async (
content
:
template
}
],
stream
:
false
stream
:
false
,
// Restricting the max tokens to 50 to avoid long titles
max_tokens
:
50
})
})
.
then
(
async
(
res
)
=>
{
...
...
@@ -314,5 +316,5 @@ export const generateTitle = async (
throw
error
;
}
return
res
?.
choices
[
0
]?.
message
?.
content
??
'
New Chat
'
;
return
res
?.
choices
[
0
]?.
message
?.
content
.
replace
(
/
[
"'
]
/g
,
''
)
??
'
New Chat
'
;
};
src/lib/apis/rag/index.ts
View file @
68c182db
...
...
@@ -33,8 +33,9 @@ type ChunkConfigForm = {
};
type
RAGConfigForm
=
{
pdf_extract_images
:
boolean
;
chunk
:
ChunkConfigForm
;
pdf_extract_images
?:
boolean
;
chunk
?:
ChunkConfigForm
;
web_loader_ssl_verification
?:
boolean
;
};
export
const
updateRAGConfig
=
async
(
token
:
string
,
payload
:
RAGConfigForm
)
=>
{
...
...
src/lib/apis/streaming/index.ts
View file @
68c182db
...
...
@@ -4,6 +4,8 @@ import type { ParsedEvent } from 'eventsource-parser';
type
TextStreamUpdate
=
{
done
:
boolean
;
value
:
string
;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
citations
?:
any
;
};
// createOpenAITextStream takes a responseBody with a SSE response,
...
...
@@ -45,6 +47,11 @@ async function* openAIStreamToIterator(
const
parsedData
=
JSON
.
parse
(
data
);
console
.
log
(
parsedData
);
if
(
parsedData
.
citations
)
{
yield
{
done
:
false
,
value
:
''
,
citations
:
parsedData
.
citations
};
continue
;
}
yield
{
done
:
false
,
value
:
parsedData
.
choices
?.[
0
]?.
delta
?.
content
??
''
};
}
catch
(
e
)
{
console
.
error
(
'
Error extracting delta from SSE event:
'
,
e
);
...
...
@@ -62,6 +69,10 @@ async function* streamLargeDeltasAsRandomChunks(
yield
textStreamUpdate
;
return
;
}
if
(
textStreamUpdate
.
citations
)
{
yield
textStreamUpdate
;
continue
;
}
let
content
=
textStreamUpdate
.
value
;
if
(
content
.
length
<
5
)
{
yield
{
done
:
false
,
value
:
content
};
...
...
src/lib/components/ChangelogModal.svelte
View file @
68c182db
...
...
@@ -22,7 +22,7 @@
</script>
<Modal bind:show>
<div class="px-5 p
y
-4 dark:text-gray-300 text-gray-700">
<div class="px-5 p
t
-4 dark:text-gray-300 text-gray-700">
<div class="flex justify-between items-start">
<div class="text-xl font-bold">
{$i18n.t('What’s New in')}
...
...
@@ -57,10 +57,8 @@
</div>
</div>
<hr class=" dark:border-gray-800" />
<div class=" w-full p-4 px-5 text-gray-700 dark:text-gray-100">
<div class=" overflow-y-scroll max-h-80">
<div class=" overflow-y-scroll max-h-80
scrollbar-none
">
<div class="mb-3">
{#if changelog}
{#each Object.keys(changelog) as version}
...
...
src/lib/components/admin/AddUserModal.svelte
View file @
68c182db
...
...
@@ -107,7 +107,7 @@
reader.readAsText(file);
} else {
toast.error(
`
File not found.
`
);
toast.error(
$i18n.t('
File not found.
')
);
}
}
};
...
...
src/lib/components/chat/MessageInput/Documents.svelte
View file @
68c182db
...
...
@@ -24,7 +24,7 @@
{
name: 'All Documents',
type: 'collection',
title: 'All Documents',
title:
$i18n.t(
'All Documents'
)
,
collection_names: $documents.map((doc) => doc.collection_name)
}
]
...
...
src/lib/components/chat/MessageInput/Suggestions.svelte
View file @
68c182db
<script lang="ts">
import Bolt from '$lib/components/icons/Bolt.svelte';
import { onMount } from 'svelte';
import { onMount, getContext } from 'svelte';
const i18n = getContext('i18n');
export let submitPrompt: Function;
export let suggestionPrompts = [];
...
...
@@ -33,7 +35,7 @@
{#if prompts.length > 0}
<div class="mb-2 flex gap-1 text-sm font-medium items-center text-gray-400 dark:text-gray-600">
<Bolt />
Suggested
{$i18n.t('
Suggested
')}
</div>
{/if}
...
...
@@ -71,7 +73,7 @@
<div
class="text-xs text-gray-400 group-hover:text-gray-500 dark:text-gray-600 dark:group-hover:text-gray-500 transition self-center"
>
Prompt
{$i18n.t('
Prompt
')}
</div>
<div
...
...
src/lib/components/chat/Messages/CitationsModal.svelte
0 → 100644
View file @
68c182db
<script lang="ts">
import { getContext, onMount, tick } from 'svelte';
import Modal from '$lib/components/common/Modal.svelte';
const i18n = getContext('i18n');
export let show = false;
export let citation;
let mergedDocuments = [];
$: if (citation) {
mergedDocuments = citation.document?.map((c, i) => {
return {
source: citation.source,
document: c,
metadata: citation.metadata?.[i]
};
});
}
</script>
<Modal size="lg" bind:show>
<div>
<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-2">
<div class=" text-lg font-medium self-center capitalize">
{$i18n.t('Citation')}
</div>
<button
class="self-center"
on:click={() => {
show = false;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-5 h-5"
>
<path
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
/>
</svg>
</button>
</div>
<div class="flex flex-col md:flex-row w-full px-6 pb-5 md:space-x-4">
<div
class="flex flex-col w-full dark:text-gray-200 overflow-y-scroll max-h-[22rem] scrollbar-none"
>
{#each mergedDocuments as document, documentIdx}
<div class="flex flex-col w-full">
<div class="text-sm font-medium dark:text-gray-300">
{$i18n.t('Source')}
</div>
<div class="text-sm dark:text-gray-400">
{document.source?.name ?? $i18n.t('No source available')}
</div>
</div>
<div class="flex flex-col w-full">
<div class=" text-sm font-medium dark:text-gray-300">
{$i18n.t('Content')}
</div>
<pre class="text-sm dark:text-gray-400 whitespace-pre-line">
{document.document}
</pre>
</div>
{#if documentIdx !== mergedDocuments.length - 1}
<hr class=" dark:border-gray-850 my-3" />
{/if}
{/each}
</div>
</div>
</div>
</Modal>
src/lib/components/chat/Messages/RateComment.svelte
View file @
68c182db
...
...
@@ -123,7 +123,7 @@
submitHandler();
}}
>
Submit
{$i18n.t('
Submit
')}
</button>
</div>
</div>
src/lib/components/chat/Messages/ResponseMessage.svelte
View file @
68c182db
...
...
@@ -23,15 +23,16 @@
revertSanitizedResponseContent,
sanitizeResponseContent
} from '$lib/utils';
import { WEBUI_BASE_URL } from '$lib/constants';
import Name from './Name.svelte';
import ProfileImage from './ProfileImage.svelte';
import Skeleton from './Skeleton.svelte';
import CodeBlock from './CodeBlock.svelte';
import Image from '$lib/components/common/Image.svelte';
import { WEBUI_BASE_URL } from '$lib/constants';
import Tooltip from '$lib/components/common/Tooltip.svelte';
import RateComment from './RateComment.svelte';
import CitationsModal from '$lib/components/chat/Messages/CitationsModal.svelte';
export let modelfiles = [];
export let message;
...
...
@@ -65,6 +66,9 @@
let showRateComment = false;
let showCitationModal = false;
let selectedCitation = null;
$: tokens = marked.lexer(sanitizeResponseContent(message.content));
const renderer = new marked.Renderer();
...
...
@@ -324,6 +328,8 @@
});
</script>
<CitationsModal bind:show={showCitationModal} citation={selectedCitation} />
{#key message.id}
<div class=" flex w-full message-{message.id}" id="message-{message.id}">
<ProfileImage
...
...
@@ -442,6 +448,44 @@
<!-- {@html marked(message.content.replaceAll('\\', '\\\\'))} -->
{/if}
{#if message.citations}
<hr class=" dark:border-gray-800 my-1" />
<div class="my-2.5 w-full flex flex-col gap-1">
{#each message.citations.reduce((acc, citation) => {
citation.document.forEach((document, index) => {
const metadata = citation.metadata?.[index];
const id = metadata?.source ?? 'N/A';
const existingSource = acc.find((item) => item.id === id);
if (existingSource) {
existingSource.document.push(document);
existingSource.metadata.push(metadata);
} else {
acc.push( { id: id, source: citation?.source, document: [document], metadata: metadata ? [metadata] : [] } );
}
});
return acc;
}, []) as citation, idx}
<div class="flex gap-1 text-xs font-semibold">
<div>
[{idx + 1}]
</div>
<button
class="dark:text-gray-500 underline"
on:click={() => {
showCitationModal = true;
selectedCitation = citation;
}}
>
{citation.source.name}
</button>
</div>
{/each}
</div>
{/if}
{#if message.done}
<div
class=" flex justify-start space-x-1 overflow-x-auto buttons text-gray-700 dark:text-gray-500"
...
...
src/lib/components/chat/ModelSelector.svelte
View file @
68c182db
...
...
@@ -82,7 +82,7 @@
</div>
{:else}
<div class=" self-center disabled:text-gray-600 disabled:hover:text-gray-600 mr-2">
<Tooltip content=
"
Remove Model
"
>
<Tooltip content=
{$i18n.t('
Remove Model
')}
>
<button
{disabled}
on:click={() => {
...
...
src/lib/components/chat/ModelSelector/Selector.svelte
View file @
68c182db
...
...
@@ -151,7 +151,7 @@
models.set(await getModels(localStorage.token));
} else {
toast.error('Download canceled');
toast.error(
$i18n.t(
'Download canceled')
)
;
}
delete $MODEL_DOWNLOAD_POOL[sanitizedModelTag];
...
...
@@ -305,7 +305,7 @@
{:else}
<div>
<div class="block px-3 py-2 text-sm text-gray-700 dark:text-gray-100">
No results found
{$i18n.t('
No results found
')}
</div>
</div>
{/each}
...
...
@@ -317,7 +317,7 @@
pullModelHandler();
}}
>
Pull "{searchValue}" from Ollama.com
{$i18n.t(`
Pull "{
{
searchValue}
}
" from Ollama.com
`, { searchValue: searchValue })}
</button>
{/if}
...
...
@@ -368,7 +368,7 @@
</div>
<div class="mr-2 translate-y-0.5">
<Tooltip content=
"
Cancel
"
>
<Tooltip content=
{$i18n.t('
Cancel
')}
>
<button
class="text-gray-800 dark:text-gray-100"
on:click={() => {
...
...
src/lib/components/chat/Settings/Account.svelte
View file @
68c182db
...
...
@@ -447,7 +447,7 @@
{/if}
</button>
<Tooltip content=
"
Create new key
"
>
<Tooltip content=
{$i18n.t('
Create new key
')}
>
<button
class=" px-1.5 py-1 dark:hover:bg-gray-850transition rounded-lg"
on:click={() => {
...
...
@@ -479,7 +479,7 @@
>
<Plus strokeWidth="2" className=" size-3.5" />
Create new secret key</button
{$i18n.t('
Create new secret key
')}
</button
>
{/if}
</div>
...
...
src/lib/components/chat/Settings/Connections.svelte
View file @
68c182db
...
...
@@ -164,7 +164,7 @@
<div class="flex gap-1.5">
<input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder=
"
Enter URL (e.g. http://localhost:11434)
"
placeholder=
{$i18n.t('
Enter URL (e.g. http://localhost:11434)
')}
bind:value={url}
/>
...
...
Prev
1
2
3
4
5
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment