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
276b7b90
Commit
276b7b90
authored
May 26, 2024
by
Jun Siang Cheah
Browse files
Merge remote-tracking branch 'upstream/dev' into feat/backend-web-search
parents
b1265c9c
7b81271b
Changes
103
Show whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
483 additions
and
453 deletions
+483
-453
backend/space/litellm_config.yaml
backend/space/litellm_config.yaml
+0
-43
backend/start.sh
backend/start.sh
+0
-5
backend/utils/models.py
backend/utils/models.py
+1
-1
backend/utils/utils.py
backend/utils/utils.py
+1
-1
requirements-dev.lock
requirements-dev.lock
+3
-7
requirements.lock
requirements.lock
+3
-7
src/lib/apis/chats/index.ts
src/lib/apis/chats/index.ts
+32
-0
src/lib/apis/litellm/index.ts
src/lib/apis/litellm/index.ts
+0
-151
src/lib/apis/models/index.ts
src/lib/apis/models/index.ts
+13
-4
src/lib/apis/ollama/index.ts
src/lib/apis/ollama/index.ts
+1
-1
src/lib/components/admin/Settings/Database.svelte
src/lib/components/admin/Settings/Database.svelte
+45
-6
src/lib/components/chat/Chat.svelte
src/lib/components/chat/Chat.svelte
+3
-10
src/lib/components/chat/Messages/ResponseMessage.svelte
src/lib/components/chat/Messages/ResponseMessage.svelte
+8
-1
src/lib/components/chat/Settings/Chats.svelte
src/lib/components/chat/Settings/Chats.svelte
+162
-98
src/lib/components/chat/Settings/General.svelte
src/lib/components/chat/Settings/General.svelte
+1
-1
src/lib/components/chat/Settings/Models.svelte
src/lib/components/chat/Settings/Models.svelte
+21
-7
src/lib/components/common/Spinner.svelte
src/lib/components/common/Spinner.svelte
+3
-3
src/lib/components/layout/Sidebar/ArchivedChatsModal.svelte
src/lib/components/layout/Sidebar/ArchivedChatsModal.svelte
+135
-90
src/lib/components/workspace/Models.svelte
src/lib/components/workspace/Models.svelte
+48
-1
src/lib/components/workspace/Playground.svelte
src/lib/components/workspace/Playground.svelte
+3
-16
No files found.
backend/space/litellm_config.yaml
deleted
100644 → 0
View file @
b1265c9c
litellm_settings
:
drop_params
:
true
model_list
:
-
model_name
:
'
HuggingFace:
Mistral:
Mistral
7B
Instruct
v0.1'
litellm_params
:
model
:
huggingface/mistralai/Mistral-7B-Instruct-v0.1
api_key
:
os.environ/HF_TOKEN
max_tokens
:
1024
-
model_name
:
'
HuggingFace:
Mistral:
Mistral
7B
Instruct
v0.2'
litellm_params
:
model
:
huggingface/mistralai/Mistral-7B-Instruct-v0.2
api_key
:
os.environ/HF_TOKEN
max_tokens
:
1024
-
model_name
:
'
HuggingFace:
Meta:
Llama
3
8B
Instruct'
litellm_params
:
model
:
huggingface/meta-llama/Meta-Llama-3-8B-Instruct
api_key
:
os.environ/HF_TOKEN
max_tokens
:
2047
-
model_name
:
'
HuggingFace:
Mistral:
Mixtral
8x7B
Instruct
v0.1'
litellm_params
:
model
:
huggingface/mistralai/Mixtral-8x7B-Instruct-v0.1
api_key
:
os.environ/HF_TOKEN
max_tokens
:
8192
-
model_name
:
'
HuggingFace:
Microsoft:
Phi-3
Mini-4K-Instruct'
litellm_params
:
model
:
huggingface/microsoft/Phi-3-mini-4k-instruct
api_key
:
os.environ/HF_TOKEN
max_tokens
:
1024
-
model_name
:
'
HuggingFace:
Google:
Gemma
7B
1.1'
litellm_params
:
model
:
huggingface/google/gemma-1.1-7b-it
api_key
:
os.environ/HF_TOKEN
max_tokens
:
1024
-
model_name
:
'
HuggingFace:
Yi-1.5
34B
Chat'
litellm_params
:
model
:
huggingface/01-ai/Yi-1.5-34B-Chat
api_key
:
os.environ/HF_TOKEN
max_tokens
:
1024
-
model_name
:
'
HuggingFace:
Nous
Research:
Nous
Hermes
2
Mixtral
8x7B
DPO'
litellm_params
:
model
:
huggingface/NousResearch/Nous-Hermes-2-Mixtral-8x7B-DPO
api_key
:
os.environ/HF_TOKEN
max_tokens
:
2048
backend/start.sh
View file @
276b7b90
...
@@ -34,11 +34,6 @@ fi
...
@@ -34,11 +34,6 @@ fi
# Check if SPACE_ID is set, if so, configure for space
# Check if SPACE_ID is set, if so, configure for space
if
[
-n
"
$SPACE_ID
"
]
;
then
if
[
-n
"
$SPACE_ID
"
]
;
then
echo
"Configuring for HuggingFace Space deployment"
echo
"Configuring for HuggingFace Space deployment"
# Copy litellm_config.yaml with specified ownership
echo
"Copying litellm_config.yaml to the desired location with specified ownership..."
cp
-f
./space/litellm_config.yaml ./data/litellm/config.yaml
if
[
-n
"
$ADMIN_USER_EMAIL
"
]
&&
[
-n
"
$ADMIN_USER_PASSWORD
"
]
;
then
if
[
-n
"
$ADMIN_USER_EMAIL
"
]
&&
[
-n
"
$ADMIN_USER_PASSWORD
"
]
;
then
echo
"Admin user configured, creating"
echo
"Admin user configured, creating"
WEBUI_SECRET_KEY
=
"
$WEBUI_SECRET_KEY
"
uvicorn main:app
--host
"
$HOST
"
--port
"
$PORT
"
--forwarded-allow-ips
'*'
&
WEBUI_SECRET_KEY
=
"
$WEBUI_SECRET_KEY
"
uvicorn main:app
--host
"
$HOST
"
--port
"
$PORT
"
--forwarded-allow-ips
'*'
&
...
...
backend/utils/models.py
View file @
276b7b90
from
apps.web.models.models
import
Models
,
ModelModel
,
ModelForm
,
ModelResponse
from
apps.web
ui
.models.models
import
Models
,
ModelModel
,
ModelForm
,
ModelResponse
def
get_model_id_from_custom_model_id
(
id
:
str
):
def
get_model_id_from_custom_model_id
(
id
:
str
):
...
...
backend/utils/utils.py
View file @
276b7b90
from
fastapi.security
import
HTTPBearer
,
HTTPAuthorizationCredentials
from
fastapi.security
import
HTTPBearer
,
HTTPAuthorizationCredentials
from
fastapi
import
HTTPException
,
status
,
Depends
from
fastapi
import
HTTPException
,
status
,
Depends
from
apps.web.models.users
import
Users
from
apps.web
ui
.models.users
import
Users
from
pydantic
import
BaseModel
from
pydantic
import
BaseModel
from
typing
import
Union
,
Optional
from
typing
import
Union
,
Optional
...
...
requirements-dev.lock
View file @
276b7b90
...
@@ -273,7 +273,6 @@ langsmith==0.1.57
...
@@ -273,7 +273,6 @@ langsmith==0.1.57
# via langchain-community
# via langchain-community
# via langchain-core
# via langchain-core
litellm==1.37.20
litellm==1.37.20
# via litellm
# via open-webui
# via open-webui
lxml==5.2.2
lxml==5.2.2
# via unstructured
# via unstructured
...
@@ -396,7 +395,6 @@ pandas==2.2.2
...
@@ -396,7 +395,6 @@ pandas==2.2.2
# via open-webui
# via open-webui
passlib==1.7.4
passlib==1.7.4
# via open-webui
# via open-webui
# via passlib
pathspec==0.12.1
pathspec==0.12.1
# via black
# via black
peewee==3.17.5
peewee==3.17.5
...
@@ -454,7 +452,6 @@ pygments==2.18.0
...
@@ -454,7 +452,6 @@ pygments==2.18.0
pyjwt==2.8.0
pyjwt==2.8.0
# via litellm
# via litellm
# via open-webui
# via open-webui
# via pyjwt
pymysql==1.1.0
pymysql==1.1.0
# via open-webui
# via open-webui
pypandoc==1.13
pypandoc==1.13
...
@@ -559,6 +556,9 @@ scipy==1.13.0
...
@@ -559,6 +556,9 @@ scipy==1.13.0
# via sentence-transformers
# via sentence-transformers
sentence-transformers==2.7.0
sentence-transformers==2.7.0
# via open-webui
# via open-webui
setuptools==69.5.1
# via ctranslate2
# via opentelemetry-instrumentation
shapely==2.0.4
shapely==2.0.4
# via rapidocr-onnxruntime
# via rapidocr-onnxruntime
shellingham==1.5.4
shellingham==1.5.4
...
@@ -659,7 +659,6 @@ uvicorn==0.22.0
...
@@ -659,7 +659,6 @@ uvicorn==0.22.0
# via fastapi
# via fastapi
# via litellm
# via litellm
# via open-webui
# via open-webui
# via uvicorn
uvloop==0.19.0
uvloop==0.19.0
# via uvicorn
# via uvicorn
validators==0.28.1
validators==0.28.1
...
@@ -687,6 +686,3 @@ youtube-transcript-api==0.6.2
...
@@ -687,6 +686,3 @@ youtube-transcript-api==0.6.2
# via open-webui
# via open-webui
zipp==3.18.1
zipp==3.18.1
# via importlib-metadata
# via importlib-metadata
setuptools==69.5.1
# via ctranslate2
# via opentelemetry-instrumentation
requirements.lock
View file @
276b7b90
...
@@ -273,7 +273,6 @@ langsmith==0.1.57
...
@@ -273,7 +273,6 @@ langsmith==0.1.57
# via langchain-community
# via langchain-community
# via langchain-core
# via langchain-core
litellm==1.37.20
litellm==1.37.20
# via litellm
# via open-webui
# via open-webui
lxml==5.2.2
lxml==5.2.2
# via unstructured
# via unstructured
...
@@ -396,7 +395,6 @@ pandas==2.2.2
...
@@ -396,7 +395,6 @@ pandas==2.2.2
# via open-webui
# via open-webui
passlib==1.7.4
passlib==1.7.4
# via open-webui
# via open-webui
# via passlib
pathspec==0.12.1
pathspec==0.12.1
# via black
# via black
peewee==3.17.5
peewee==3.17.5
...
@@ -454,7 +452,6 @@ pygments==2.18.0
...
@@ -454,7 +452,6 @@ pygments==2.18.0
pyjwt==2.8.0
pyjwt==2.8.0
# via litellm
# via litellm
# via open-webui
# via open-webui
# via pyjwt
pymysql==1.1.0
pymysql==1.1.0
# via open-webui
# via open-webui
pypandoc==1.13
pypandoc==1.13
...
@@ -559,6 +556,9 @@ scipy==1.13.0
...
@@ -559,6 +556,9 @@ scipy==1.13.0
# via sentence-transformers
# via sentence-transformers
sentence-transformers==2.7.0
sentence-transformers==2.7.0
# via open-webui
# via open-webui
setuptools==69.5.1
# via ctranslate2
# via opentelemetry-instrumentation
shapely==2.0.4
shapely==2.0.4
# via rapidocr-onnxruntime
# via rapidocr-onnxruntime
shellingham==1.5.4
shellingham==1.5.4
...
@@ -659,7 +659,6 @@ uvicorn==0.22.0
...
@@ -659,7 +659,6 @@ uvicorn==0.22.0
# via fastapi
# via fastapi
# via litellm
# via litellm
# via open-webui
# via open-webui
# via uvicorn
uvloop==0.19.0
uvloop==0.19.0
# via uvicorn
# via uvicorn
validators==0.28.1
validators==0.28.1
...
@@ -687,6 +686,3 @@ youtube-transcript-api==0.6.2
...
@@ -687,6 +686,3 @@ youtube-transcript-api==0.6.2
# via open-webui
# via open-webui
zipp==3.18.1
zipp==3.18.1
# via importlib-metadata
# via importlib-metadata
setuptools==69.5.1
# via ctranslate2
# via opentelemetry-instrumentation
src/lib/apis/chats/index.ts
View file @
276b7b90
...
@@ -654,3 +654,35 @@ export const deleteAllChats = async (token: string) => {
...
@@ -654,3 +654,35 @@ export const deleteAllChats = async (token: string) => {
return
res
;
return
res
;
};
};
export
const
archiveAllChats
=
async
(
token
:
string
)
=>
{
let
error
=
null
;
const
res
=
await
fetch
(
`
${
WEBUI_API_BASE_URL
}
/chats/archive/all`
,
{
method
:
'
POST
'
,
headers
:
{
Accept
:
'
application/json
'
,
'
Content-Type
'
:
'
application/json
'
,
...(
token
&&
{
authorization
:
`Bearer
${
token
}
`
})
}
})
.
then
(
async
(
res
)
=>
{
if
(
!
res
.
ok
)
throw
await
res
.
json
();
return
res
.
json
();
})
.
then
((
json
)
=>
{
return
json
;
})
.
catch
((
err
)
=>
{
error
=
err
.
detail
;
console
.
log
(
err
);
return
null
;
});
if
(
error
)
{
throw
error
;
}
return
res
;
};
src/lib/apis/litellm/index.ts
deleted
100644 → 0
View file @
b1265c9c
import
{
LITELLM_API_BASE_URL
}
from
'
$lib/constants
'
;
export
const
getLiteLLMModels
=
async
(
token
:
string
=
''
)
=>
{
let
error
=
null
;
const
res
=
await
fetch
(
`
${
LITELLM_API_BASE_URL
}
/v1/models`
,
{
method
:
'
GET
'
,
headers
:
{
Accept
:
'
application/json
'
,
'
Content-Type
'
:
'
application/json
'
,
...(
token
&&
{
authorization
:
`Bearer
${
token
}
`
})
}
})
.
then
(
async
(
res
)
=>
{
if
(
!
res
.
ok
)
throw
await
res
.
json
();
return
res
.
json
();
})
.
catch
((
err
)
=>
{
console
.
log
(
err
);
error
=
`LiteLLM:
${
err
?.
error
?.
message
??
'
Network Problem
'
}
`
;
return
[];
});
if
(
error
)
{
throw
error
;
}
const
models
=
Array
.
isArray
(
res
)
?
res
:
res
?.
data
??
null
;
return
models
?
models
.
map
((
model
)
=>
({
id
:
model
.
id
,
name
:
model
.
name
??
model
.
id
,
external
:
true
,
source
:
'
LiteLLM
'
,
custom_info
:
model
.
custom_info
}))
.
sort
((
a
,
b
)
=>
{
return
a
.
name
.
localeCompare
(
b
.
name
);
})
:
models
;
};
export
const
getLiteLLMModelInfo
=
async
(
token
:
string
=
''
)
=>
{
let
error
=
null
;
const
res
=
await
fetch
(
`
${
LITELLM_API_BASE_URL
}
/model/info`
,
{
method
:
'
GET
'
,
headers
:
{
Accept
:
'
application/json
'
,
'
Content-Type
'
:
'
application/json
'
,
...(
token
&&
{
authorization
:
`Bearer
${
token
}
`
})
}
})
.
then
(
async
(
res
)
=>
{
if
(
!
res
.
ok
)
throw
await
res
.
json
();
return
res
.
json
();
})
.
catch
((
err
)
=>
{
console
.
log
(
err
);
error
=
`LiteLLM:
${
err
?.
error
?.
message
??
'
Network Problem
'
}
`
;
return
[];
});
if
(
error
)
{
throw
error
;
}
const
models
=
Array
.
isArray
(
res
)
?
res
:
res
?.
data
??
null
;
return
models
;
};
type
AddLiteLLMModelForm
=
{
name
:
string
;
model
:
string
;
api_base
:
string
;
api_key
:
string
;
rpm
:
string
;
max_tokens
:
string
;
};
export
const
addLiteLLMModel
=
async
(
token
:
string
=
''
,
payload
:
AddLiteLLMModelForm
)
=>
{
let
error
=
null
;
const
res
=
await
fetch
(
`
${
LITELLM_API_BASE_URL
}
/model/new`
,
{
method
:
'
POST
'
,
headers
:
{
Accept
:
'
application/json
'
,
'
Content-Type
'
:
'
application/json
'
,
...(
token
&&
{
authorization
:
`Bearer
${
token
}
`
})
},
body
:
JSON
.
stringify
({
model_name
:
payload
.
name
,
litellm_params
:
{
model
:
payload
.
model
,
...(
payload
.
api_base
===
''
?
{}
:
{
api_base
:
payload
.
api_base
}),
...(
payload
.
api_key
===
''
?
{}
:
{
api_key
:
payload
.
api_key
}),
...(
isNaN
(
parseInt
(
payload
.
rpm
))
?
{}
:
{
rpm
:
parseInt
(
payload
.
rpm
)
}),
...(
payload
.
max_tokens
===
''
?
{}
:
{
max_tokens
:
payload
.
max_tokens
})
}
})
})
.
then
(
async
(
res
)
=>
{
if
(
!
res
.
ok
)
throw
await
res
.
json
();
return
res
.
json
();
})
.
catch
((
err
)
=>
{
console
.
log
(
err
);
error
=
`LiteLLM:
${
err
?.
error
?.
message
??
'
Network Problem
'
}
`
;
return
[];
});
if
(
error
)
{
throw
error
;
}
return
res
;
};
export
const
deleteLiteLLMModel
=
async
(
token
:
string
=
''
,
id
:
string
)
=>
{
let
error
=
null
;
const
res
=
await
fetch
(
`
${
LITELLM_API_BASE_URL
}
/model/delete`
,
{
method
:
'
POST
'
,
headers
:
{
Accept
:
'
application/json
'
,
'
Content-Type
'
:
'
application/json
'
,
...(
token
&&
{
authorization
:
`Bearer
${
token
}
`
})
},
body
:
JSON
.
stringify
({
id
:
id
})
})
.
then
(
async
(
res
)
=>
{
if
(
!
res
.
ok
)
throw
await
res
.
json
();
return
res
.
json
();
})
.
catch
((
err
)
=>
{
console
.
log
(
err
);
error
=
`LiteLLM:
${
err
?.
error
?.
message
??
'
Network Problem
'
}
`
;
return
[];
});
if
(
error
)
{
throw
error
;
}
return
res
;
};
src/lib/apis/models/index.ts
View file @
276b7b90
...
@@ -32,7 +32,7 @@ export const addNewModel = async (token: string, model: object) => {
...
@@ -32,7 +32,7 @@ export const addNewModel = async (token: string, model: object) => {
export
const
getModelInfos
=
async
(
token
:
string
=
''
)
=>
{
export
const
getModelInfos
=
async
(
token
:
string
=
''
)
=>
{
let
error
=
null
;
let
error
=
null
;
const
res
=
await
fetch
(
`
${
WEBUI_API_BASE_URL
}
/models
/
`
,
{
const
res
=
await
fetch
(
`
${
WEBUI_API_BASE_URL
}
/models`
,
{
method
:
'
GET
'
,
method
:
'
GET
'
,
headers
:
{
headers
:
{
Accept
:
'
application/json
'
,
Accept
:
'
application/json
'
,
...
@@ -63,7 +63,10 @@ export const getModelInfos = async (token: string = '') => {
...
@@ -63,7 +63,10 @@ export const getModelInfos = async (token: string = '') => {
export
const
getModelById
=
async
(
token
:
string
,
id
:
string
)
=>
{
export
const
getModelById
=
async
(
token
:
string
,
id
:
string
)
=>
{
let
error
=
null
;
let
error
=
null
;
const
res
=
await
fetch
(
`
${
WEBUI_API_BASE_URL
}
/models/
${
id
}
`
,
{
const
searchParams
=
new
URLSearchParams
();
searchParams
.
append
(
'
id
'
,
id
);
const
res
=
await
fetch
(
`
${
WEBUI_API_BASE_URL
}
/models?
${
searchParams
.
toString
()}
`
,
{
method
:
'
GET
'
,
method
:
'
GET
'
,
headers
:
{
headers
:
{
Accept
:
'
application/json
'
,
Accept
:
'
application/json
'
,
...
@@ -95,7 +98,10 @@ export const getModelById = async (token: string, id: string) => {
...
@@ -95,7 +98,10 @@ export const getModelById = async (token: string, id: string) => {
export
const
updateModelById
=
async
(
token
:
string
,
id
:
string
,
model
:
object
)
=>
{
export
const
updateModelById
=
async
(
token
:
string
,
id
:
string
,
model
:
object
)
=>
{
let
error
=
null
;
let
error
=
null
;
const
res
=
await
fetch
(
`
${
WEBUI_API_BASE_URL
}
/models/
${
id
}
/update`
,
{
const
searchParams
=
new
URLSearchParams
();
searchParams
.
append
(
'
id
'
,
id
);
const
res
=
await
fetch
(
`
${
WEBUI_API_BASE_URL
}
/models/update?
${
searchParams
.
toString
()}
`
,
{
method
:
'
POST
'
,
method
:
'
POST
'
,
headers
:
{
headers
:
{
Accept
:
'
application/json
'
,
Accept
:
'
application/json
'
,
...
@@ -128,7 +134,10 @@ export const updateModelById = async (token: string, id: string, model: object)
...
@@ -128,7 +134,10 @@ export const updateModelById = async (token: string, id: string, model: object)
export
const
deleteModelById
=
async
(
token
:
string
,
id
:
string
)
=>
{
export
const
deleteModelById
=
async
(
token
:
string
,
id
:
string
)
=>
{
let
error
=
null
;
let
error
=
null
;
const
res
=
await
fetch
(
`
${
WEBUI_API_BASE_URL
}
/models/
${
id
}
/delete`
,
{
const
searchParams
=
new
URLSearchParams
();
searchParams
.
append
(
'
id
'
,
id
);
const
res
=
await
fetch
(
`
${
WEBUI_API_BASE_URL
}
/models/delete?
${
searchParams
.
toString
()}
`
,
{
method
:
'
DELETE
'
,
method
:
'
DELETE
'
,
headers
:
{
headers
:
{
Accept
:
'
application/json
'
,
Accept
:
'
application/json
'
,
...
...
src/lib/apis/ollama/index.ts
View file @
276b7b90
...
@@ -164,7 +164,7 @@ export const getOllamaVersion = async (token: string = '') => {
...
@@ -164,7 +164,7 @@ export const getOllamaVersion = async (token: string = '') => {
throw
error
;
throw
error
;
}
}
return
res
?.
version
??
''
;
return
res
?.
version
??
false
;
};
};
export
const
getOllamaModels
=
async
(
token
:
string
=
''
)
=>
{
export
const
getOllamaModels
=
async
(
token
:
string
=
''
)
=>
{
...
...
src/lib/components/admin/Settings/Database.svelte
View file @
276b7b90
<script lang="ts">
<script lang="ts">
import fileSaver from 'file-saver';
const { saveAs } = fileSaver;
import { downloadDatabase } from '$lib/apis/utils';
import { downloadDatabase } from '$lib/apis/utils';
import { onMount, getContext } from 'svelte';
import { onMount, getContext } from 'svelte';
import { config } from '$lib/stores';
import { config
, user
} from '$lib/stores';
import { toast } from 'svelte-sonner';
import { toast } from 'svelte-sonner';
import { getAllUserChats } from '$lib/apis/chats';
const i18n = getContext('i18n');
const i18n = getContext('i18n');
export let saveHandler: Function;
export let saveHandler: Function;
const exportAllUserChats = async () => {
let blob = new Blob([JSON.stringify(await getAllUserChats(localStorage.token))], {
type: 'application/json'
});
saveAs(blob, `all-chats-export-${Date.now()}.json`);
};
onMount(async () => {
onMount(async () => {
// permissions = await getUserPermissions(localStorage.token);
// permissions = await getUserPermissions(localStorage.token);
});
});
...
@@ -23,10 +34,10 @@
...
@@ -23,10 +34,10 @@
<div>
<div>
<div class=" mb-2 text-sm font-medium">{$i18n.t('Database')}</div>
<div class=" mb-2 text-sm font-medium">{$i18n.t('Database')}</div>
{#if $config?.enable_admin_export ?? true}
<div class=" flex w-full justify-between">
<div class=" flex w-full justify-between">
<!-- <div class=" self-center text-xs font-medium">{$i18n.t('Allow Chat Deletion')}</div> -->
<!-- <div class=" self-center text-xs font-medium">{$i18n.t('Allow Chat Deletion')}</div> -->
{#if $config?.admin_export_enabled ?? true}
<button
<button
class=" flex rounded-md py-1.5 px-3 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
class=" flex rounded-md py-1.5 px-3 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
type="button"
type="button"
...
@@ -55,8 +66,36 @@
...
@@ -55,8 +66,36 @@
</div>
</div>
<div class=" self-center text-sm font-medium">{$i18n.t('Download Database')}</div>
<div class=" self-center text-sm font-medium">{$i18n.t('Download Database')}</div>
</button>
</button>
{/if}
</div>
</div>
<hr class=" dark:border-gray-700 my-1" />
<button
class=" flex rounded-md py-2 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
on:click={() => {
exportAllUserChats();
}}
>
<div class=" self-center mr-3">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path d="M2 3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3Z" />
<path
fill-rule="evenodd"
d="M13 6H3v6a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V6ZM8.75 7.75a.75.75 0 0 0-1.5 0v2.69L6.03 9.22a.75.75 0 0 0-1.06 1.06l2.5 2.5a.75.75 0 0 0 1.06 0l2.5-2.5a.75.75 0 1 0-1.06-1.06l-1.22 1.22V7.75Z"
clip-rule="evenodd"
/>
</svg>
</div>
<div class=" self-center text-sm font-medium">
{$i18n.t('Export All Chats (All Users)')}
</div>
</button>
{/if}
</div>
</div>
</div>
</div>
...
...
src/lib/components/chat/Chat.svelte
View file @
276b7b90
...
@@ -39,12 +39,7 @@
...
@@ -39,12 +39,7 @@
import
MessageInput
from
'$lib/components/chat/MessageInput.svelte'
;
import
MessageInput
from
'$lib/components/chat/MessageInput.svelte'
;
import
Messages
from
'$lib/components/chat/Messages.svelte'
;
import
Messages
from
'$lib/components/chat/Messages.svelte'
;
import
Navbar
from
'$lib/components/layout/Navbar.svelte'
;
import
Navbar
from
'$lib/components/layout/Navbar.svelte'
;
import
{
import
{
OLLAMA_API_BASE_URL
,
OPENAI_API_BASE_URL
,
WEBUI_BASE_URL
}
from
'$lib/constants'
;
LITELLM_API_BASE_URL
,
OLLAMA_API_BASE_URL
,
OPENAI_API_BASE_URL
,
WEBUI_BASE_URL
}
from
'$lib/constants'
;
import
{
createOpenAITextStream
}
from
'$lib/apis/streaming'
;
import
{
createOpenAITextStream
}
from
'$lib/apis/streaming'
;
import
{
queryMemory
}
from
'$lib/apis/memories'
;
import
{
queryMemory
}
from
'$lib/apis/memories'
;
import
type
{
Writable
}
from
'svelte/store'
;
import
type
{
Writable
}
from
'svelte/store'
;
...
@@ -779,9 +774,7 @@
...
@@ -779,9 +774,7 @@
docs: docs.length > 0 ? docs : undefined,
docs: docs.length > 0 ? docs : undefined,
citations: docs.length > 0
citations: docs.length > 0
},
},
model?.source?.toLowerCase() === '
litellm
'
`${OPENAI_API_BASE_URL}`
? `${LITELLM_API_BASE_URL}/v1`
: `${OPENAI_API_BASE_URL}`
);
);
// Wait until history/message have been updated
// Wait until history/message have been updated
...
@@ -1120,6 +1113,6 @@
...
@@ -1120,6 +1113,6 @@
{messages}
{messages}
{submitPrompt}
{submitPrompt}
{stopResponse}
{stopResponse}
webSearchAvailable={$config.websearch ?? false}
webSearchAvailable={$config.
enable_
websearch ?? false}
/>
/>
{/if}
{/if}
src/lib/components/chat/Messages/ResponseMessage.svelte
View file @
276b7b90
...
@@ -80,6 +80,13 @@
...
@@ -80,6 +80,13 @@
return `<code>${code.replaceAll('&', '&')}</code>`;
return `<code>${code.replaceAll('&', '&')}</code>`;
};
};
// Open all links in a new tab/window (from https://github.com/markedjs/marked/issues/655#issuecomment-383226346)
const origLinkRenderer = renderer.link;
renderer.link = (href, title, text) => {
const html = origLinkRenderer.call(renderer, href, title, text);
return html.replace(/^<a /, '<a target="_blank" rel="nofollow" ');
};
const { extensions, ...defaults } = marked.getDefaults() as marked.MarkedOptions & {
const { extensions, ...defaults } = marked.getDefaults() as marked.MarkedOptions & {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
// eslint-disable-next-line @typescript-eslint/no-explicit-any
extensions: any;
extensions: any;
...
@@ -790,7 +797,7 @@
...
@@ -790,7 +797,7 @@
</button>
</button>
</Tooltip>
</Tooltip>
{#if $config.
images
&& !readOnly}
{#if $config.
enable_image_generation
&& !readOnly}
<Tooltip content="Generate Image" placement="bottom">
<Tooltip content="Generate Image" placement="bottom">
<button
<button
class="{isLastMessage
class="{isLastMessage
...
...
src/lib/components/chat/Settings/Chats.svelte
View file @
276b7b90
...
@@ -5,6 +5,7 @@
...
@@ -5,6 +5,7 @@
import { chats, user, config } from '$lib/stores';
import { chats, user, config } from '$lib/stores';
import {
import {
archiveAllChats,
createNewChat,
createNewChat,
deleteAllChats,
deleteAllChats,
getAllChats,
getAllChats,
...
@@ -22,7 +23,10 @@
...
@@ -22,7 +23,10 @@
// Chats
// Chats
let saveChatHistory = true;
let saveChatHistory = true;
let importFiles;
let importFiles;
let showArchiveConfirm = false;
let showDeleteConfirm = false;
let showDeleteConfirm = false;
let chatImportInputElement: HTMLInputElement;
let chatImportInputElement: HTMLInputElement;
$: if (importFiles) {
$: if (importFiles) {
...
@@ -68,14 +72,15 @@
...
@@ -68,14 +72,15 @@
saveAs(blob, `chat-export-${Date.now()}.json`);
saveAs(blob, `chat-export-${Date.now()}.json`);
};
};
const exportAllUserChats = async () => {
const archiveAllChatsHandler = async () => {
let blob = new Blob([JSON.stringify(await getAllUserChats(localStorage.token))], {
await goto('/');
type: 'application/json'
await archiveAllChats(localStorage.token).catch((error) => {
toast.error(error);
});
});
saveAs(blob, `all-chats-export-${Date.now()}.json`
);
await chats.set(await getChatList(localStorage.token)
);
};
};
const deleteChats = async () => {
const delete
All
Chats
Handler
= async () => {
await goto('/');
await goto('/');
await deleteAllChats(localStorage.token).catch((error) => {
await deleteAllChats(localStorage.token).catch((error) => {
toast.error(error);
toast.error(error);
...
@@ -217,7 +222,8 @@
...
@@ -217,7 +222,8 @@
<hr class=" dark:border-gray-700" />
<hr class=" dark:border-gray-700" />
{#if showDeleteConfirm}
<div class="flex flex-col">
{#if showArchiveConfirm}
<div class="flex justify-between rounded-md items-center py-2 px-3.5 w-full transition">
<div class="flex justify-between rounded-md items-center py-2 px-3.5 w-full transition">
<div class="flex items-center space-x-3">
<div class="flex items-center space-x-3">
<svg
<svg
...
@@ -240,8 +246,8 @@
...
@@ -240,8 +246,8 @@
<button
<button
class="hover:text-white transition"
class="hover:text-white transition"
on:click={() => {
on:click={() => {
deleteChats
();
archiveAllChatsHandler
();
show
Delet
eConfirm = false;
show
Archiv
eConfirm = false;
}}
}}
>
>
<svg
<svg
...
@@ -260,7 +266,7 @@
...
@@ -260,7 +266,7 @@
<button
<button
class="hover:text-white transition"
class="hover:text-white transition"
on:click={() => {
on:click={() => {
show
Delet
eConfirm = false;
show
Archiv
eConfirm = false;
}}
}}
>
>
<svg
<svg
...
@@ -280,34 +286,94 @@
...
@@ -280,34 +286,94 @@
<button
<button
class=" flex rounded-md py-2 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
class=" flex rounded-md py-2 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
on:click={() => {
on:click={() => {
show
Delet
eConfirm = true;
show
Archiv
eConfirm = true;
}}
}}
>
>
<div class=" self-center mr-3">
<div class=" self-center mr-3">
<svg
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0
16 16
"
viewBox="0 0
24 24
"
fill="currentColor"
fill="currentColor"
class="
w-4 h
-4"
class="
size
-4"
>
>
<path
d="M3.375 3C2.339 3 1.5 3.84 1.5 4.875v.75c0 1.036.84 1.875 1.875 1.875h17.25c1.035 0 1.875-.84 1.875-1.875v-.75C22.5 3.839 21.66 3 20.625 3H3.375Z"
/>
<path
<path
fill-rule="evenodd"
fill-rule="evenodd"
d="
M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm7 7
a.75.75 0 0 1
-.75.7
5h-4
.5
a.75.75 0 0 1
0-1.5h4.5A
.75.75
0 0 1 11 9
Z"
d="
m3.087 9 .54 9.176A3 3 0 0 0 6.62 21h10.757a3 3 0 0 0 2.995-2.824L20.913 9H3.087Zm6.163 3.75A.75.75 0 0 1 10 12h4
a.75.75 0 0 1
0 1.
5h-4a.75.75 0 0 1
-
.75
-
.75Z"
clip-rule="evenodd"
clip-rule="evenodd"
/>
/>
</svg>
</svg>
</div>
</div>
<div class=" self-center text-sm font-medium">{$i18n.t('
Delete
Chats')}</div>
<div class=" self-center text-sm font-medium">{$i18n.t('
Archive All
Chats')}</div>
</button>
</button>
{/if}
{/if}
{#if $user?.role === 'admin' && ($config?.admin_export_enabled ?? true)}
{#if showDeleteConfirm}
<hr class=" dark:border-gray-700" />
<div class="flex justify-between rounded-md items-center py-2 px-3.5 w-full transition">
<div class="flex items-center space-x-3">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path d="M2 3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3Z" />
<path
fill-rule="evenodd"
d="M13 6H3v6a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V6ZM5.72 7.47a.75.75 0 0 1 1.06 0L8 8.69l1.22-1.22a.75.75 0 1 1 1.06 1.06L9.06 9.75l1.22 1.22a.75.75 0 1 1-1.06 1.06L8 10.81l-1.22 1.22a.75.75 0 0 1-1.06-1.06l1.22-1.22-1.22-1.22a.75.75 0 0 1 0-1.06Z"
clip-rule="evenodd"
/>
</svg>
<span>{$i18n.t('Are you sure?')}</span>
</div>
<div class="flex space-x-1.5 items-center">
<button
class="hover:text-white transition"
on:click={() => {
deleteAllChatsHandler();
showDeleteConfirm = false;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
clip-rule="evenodd"
/>
</svg>
</button>
<button
class="hover:text-white transition"
on:click={() => {
showDeleteConfirm = false;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4"
>
<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>
{:else}
<button
<button
class=" flex rounded-md py-2 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
class=" flex rounded-md py-2 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
on:click={() => {
on:click={() => {
exportAllUserChats()
;
showDeleteConfirm = true
;
}}
}}
>
>
<div class=" self-center mr-3">
<div class=" self-center mr-3">
...
@@ -317,18 +383,16 @@
...
@@ -317,18 +383,16 @@
fill="currentColor"
fill="currentColor"
class="w-4 h-4"
class="w-4 h-4"
>
>
<path d="M2 3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3Z" />
<path
<path
fill-rule="evenodd"
fill-rule="evenodd"
d="M
13 6H3v6a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V6ZM8.75 7.75a.75.75 0 0 0-1.5 0v2.69L6.03 9.22a.75.75 0 0 0-1.06 1.06l2.5 2.5a.75.75 0 0 0 1.06 0l2.5-2
.5a.75.75 0 1 0-1.
06-1.06l-1.22 1.22V7.75
Z"
d="M
4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm7 7a.75.75 0 0 1-.75.75h-4
.5a.75.75 0
0
1 0-1.
5h4.5A.75.75 0 0 1 11 9
Z"
clip-rule="evenodd"
clip-rule="evenodd"
/>
/>
</svg>
</svg>
</div>
</div>
<div class=" self-center text-sm font-medium">
<div class=" self-center text-sm font-medium">{$i18n.t('Delete All Chats')}</div>
{$i18n.t('Export All Chats (All Users)')}
</div>
</button>
</button>
{/if}
{/if}
</div>
</div>
</div>
</div>
</div>
src/lib/components/chat/Settings/General.svelte
View file @
276b7b90
...
@@ -302,7 +302,7 @@
...
@@ -302,7 +302,7 @@
system: system !== '' ? system : undefined,
system: system !== '' ? system : undefined,
params: {
params: {
seed: (params.seed !== 0 ? params.seed : undefined) ?? undefined,
seed: (params.seed !== 0 ? params.seed : undefined) ?? undefined,
stop: params.stop
!== null
? params.stop.split(',').filter((e) => e) : undefined,
stop: params.stop ? params.stop.split(',').filter((e) => e) : undefined,
temperature: params.temperature !== '' ? params.temperature : undefined,
temperature: params.temperature !== '' ? params.temperature : undefined,
frequency_penalty:
frequency_penalty:
params.frequency_penalty !== '' ? params.frequency_penalty : undefined,
params.frequency_penalty !== '' ? params.frequency_penalty : undefined,
...
...
src/lib/components/chat/Settings/Models.svelte
View file @
276b7b90
...
@@ -18,6 +18,7 @@
...
@@ -18,6 +18,7 @@
import { onMount, getContext } from 'svelte';
import { onMount, getContext } from 'svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte';
import Spinner from '$lib/components/common/Spinner.svelte';
const i18n = getContext('i18n');
const i18n = getContext('i18n');
...
@@ -34,7 +35,8 @@
...
@@ -34,7 +35,8 @@
let updateProgress = null;
let updateProgress = null;
let showExperimentalOllama = false;
let showExperimentalOllama = false;
let ollamaVersion = '';
let ollamaVersion = null;
const MAX_PARALLEL_DOWNLOADS = 3;
const MAX_PARALLEL_DOWNLOADS = 3;
let modelTransferring = false;
let modelTransferring = false;
...
@@ -56,8 +58,11 @@
...
@@ -56,8 +58,11 @@
const updateModelsHandler = async () => {
const updateModelsHandler = async () => {
for (const model of $models.filter(
for (const model of $models.filter(
(m) =>
(m) =>
m.size != null &&
!(m?.preset ?? false) &&
(selectedOllamaUrlIdx === null ? true : (m?.urls ?? []).includes(selectedOllamaUrlIdx))
m.owned_by === 'ollama' &&
(selectedOllamaUrlIdx === null
? true
: (m?.ollama?.urls ?? []).includes(selectedOllamaUrlIdx))
)) {
)) {
console.log(model);
console.log(model);
...
@@ -446,7 +451,7 @@
...
@@ -446,7 +451,7 @@
<div class="flex flex-col h-full justify-between text-sm">
<div class="flex flex-col h-full justify-between text-sm">
<div class=" space-y-3 pr-1.5 overflow-y-scroll h-[24rem]">
<div class=" space-y-3 pr-1.5 overflow-y-scroll h-[24rem]">
{#if ollamaVersion}
{#if ollamaVersion
!== null
}
<div class="space-y-2 pr-1.5">
<div class="space-y-2 pr-1.5">
<div class="text-sm font-medium">{$i18n.t('Manage Ollama Models')}</div>
<div class="text-sm font-medium">{$i18n.t('Manage Ollama Models')}</div>
...
@@ -644,9 +649,12 @@
...
@@ -644,9 +649,12 @@
{#if !deleteModelTag}
{#if !deleteModelTag}
<option value="" disabled selected>{$i18n.t('Select a model')}</option>
<option value="" disabled selected>{$i18n.t('Select a model')}</option>
{/if}
{/if}
{#each $models.filter((m) =>
m.size != null
&& (selectedOllamaUrlIdx === null ? true : (m?.urls ?? []).includes(selectedOllamaUrlIdx))) as model}
{#each $models.filter((m) =>
!(m?.preset ?? false) && m.owned_by === 'ollama'
&& (selectedOllamaUrlIdx === null ? true : (m?.
ollama?.
urls ?? []).includes(selectedOllamaUrlIdx))) as model}
<option value={model.name} class="bg-gray-100 dark:bg-gray-700"
<option value={model.name} class="bg-gray-100 dark:bg-gray-700"
>{model.name + ' (' + (model.size / 1024 ** 3).toFixed(1) + ' GB)'}</option
>{model.name +
' (' +
(model.ollama.size / 1024 ** 3).toFixed(1) +
' GB)'}</option
>
>
{/each}
{/each}
</select>
</select>
...
@@ -874,8 +882,14 @@
...
@@ -874,8 +882,14 @@
{/if}
{/if}
</div>
</div>
</div>
</div>
{:else}
{:else
if ollamaVersion === false
}
<div>Ollama Not Detected</div>
<div>Ollama Not Detected</div>
{:else}
<div class="flex h-full justify-center">
<div class="my-auto">
<Spinner className="size-6" />
</div>
</div>
{/if}
{/if}
</div>
</div>
</div>
</div>
src/lib/components/common/Spinner.svelte
View file @
276b7b90
<script lang="ts">
<script lang="ts">
export let className: string = '';
export let className: string = '
size-5
';
</script>
</script>
<div class="flex justify-center text-center
{className}
">
<div class="flex justify-center text-center">
<svg class=
"size-5"
viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"
<svg class=
{className}
viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"
><style>
><style>
.spinner_ajPY {
.spinner_ajPY {
transform-origin: center;
transform-origin: center;
...
...
src/lib/components/layout/Sidebar/ArchivedChatsModal.svelte
View file @
276b7b90
<script lang="ts">
<script lang="ts">
import fileSaver from 'file-saver';
const { saveAs } = fileSaver;
import { toast } from 'svelte-sonner';
import { toast } from 'svelte-sonner';
import dayjs from 'dayjs';
import dayjs from 'dayjs';
import { getContext, createEventDispatcher } from 'svelte';
import { getContext, createEventDispatcher } from 'svelte';
...
@@ -13,6 +15,8 @@
...
@@ -13,6 +15,8 @@
export let show = false;
export let show = false;
let searchValue = '';
let chats = [];
let chats = [];
const unarchiveChatHandler = async (chatId) => {
const unarchiveChatHandler = async (chatId) => {
...
@@ -33,6 +37,13 @@
...
@@ -33,6 +37,13 @@
chats = await getArchivedChatList(localStorage.token);
chats = await getArchivedChatList(localStorage.token);
};
};
const exportChatsHandler = async () => {
let blob = new Blob([JSON.stringify(chats)], {
type: 'application/json'
});
saveAs(blob, `archived-chat-export-${Date.now()}.json`);
};
$: if (show) {
$: if (show) {
(async () => {
(async () => {
chats = await getArchivedChatList(localStorage.token);
chats = await getArchivedChatList(localStorage.token);
...
@@ -63,10 +74,35 @@
...
@@ -63,10 +74,35 @@
</button>
</button>
</div>
</div>
<div class="flex flex-col md:flex-row w-full px-5 pb-4 md:space-x-4 dark:text-gray-200">
<div class="flex flex-col w-full px-5 pb-4 dark:text-gray-200">
<div class=" flex w-full mt-2 space-x-2">
<div class="flex flex-1">
<div class=" self-center ml-1 mr-3">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z"
clip-rule="evenodd"
/>
</svg>
</div>
<input
class=" w-full text-sm pr-4 py-1 rounded-r-xl outline-none bg-transparent"
bind:value={searchValue}
placeholder={$i18n.t('Search Chats')}
/>
</div>
</div>
<hr class=" dark:border-gray-850 my-2" />
<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
{#if chats.length > 0}
{#if chats.length > 0}
<div class="text-left text-sm w-full mb-4 max-h-[22rem] overflow-y-scroll">
<div>
<div class="text-left text-sm w-full mb-3 max-h-[22rem] overflow-y-scroll">
<div class="relative overflow-x-auto">
<div class="relative overflow-x-auto">
<table class="w-full text-sm text-left text-gray-600 dark:text-gray-400 table-auto">
<table class="w-full text-sm text-left text-gray-600 dark:text-gray-400 table-auto">
<thead
<thead
...
@@ -74,12 +110,16 @@
...
@@ -74,12 +110,16 @@
>
>
<tr>
<tr>
<th scope="col" class="px-3 py-2"> {$i18n.t('Name')} </th>
<th scope="col" class="px-3 py-2"> {$i18n.t('Name')} </th>
<th scope="col" class="px-3 py-2 hidden md:flex"> {$i18n.t('Created At')} </th>
<th scope="col" class="px-3 py-2 hidden md:flex">
{$i18n.t('Created At')}
</th>
<th scope="col" class="px-3 py-2 text-right" />
<th scope="col" class="px-3 py-2 text-right" />
</tr>
</tr>
</thead>
</thead>
<tbody>
<tbody>
{#each chats as chat, idx}
{#each chats.filter((c) => searchValue === '' || c.title
.toLowerCase()
.includes(searchValue.toLowerCase())) as chat, idx}
<tr
<tr
class="bg-transparent {idx !== chats.length - 1 &&
class="bg-transparent {idx !== chats.length - 1 &&
'border-b'} dark:bg-gray-900 dark:border-gray-850 text-xs"
'border-b'} dark:bg-gray-900 dark:border-gray-850 text-xs"
...
@@ -154,11 +194,16 @@
...
@@ -154,11 +194,16 @@
</tbody>
</tbody>
</table>
</table>
</div>
</div>
<!-- {#each chats as chat}
<div>
{JSON.stringify(chat)}
</div>
</div>
{/each} -->
<div class="flex flex-wrap text-sm font-medium gap-1.5 mt-2 m-1">
<button
class=" px-3.5 py-1.5 font-medium hover:bg-black/5 dark:hover:bg-white/5 outline outline-1 outline-gray-300 dark:outline-gray-800 rounded-3xl"
on:click={() => {
exportChatsHandler();
}}>Export All Archived Chats</button
>
</div>
</div>
</div>
{:else}
{:else}
<div class="text-left text-sm w-full mb-8">
<div class="text-left text-sm w-full mb-8">
...
...
src/lib/components/workspace/Models.svelte
View file @
276b7b90
...
@@ -20,6 +20,8 @@
...
@@ -20,6 +20,8 @@
let importFiles;
let importFiles;
let modelsImportInputElement: HTMLInputElement;
let modelsImportInputElement: HTMLInputElement;
let searchValue = '';
const deleteModelHandler = async (model) => {
const deleteModelHandler = async (model) => {
console.log(model.info);
console.log(model.info);
if (!model?.info) {
if (!model?.info) {
...
@@ -97,6 +99,49 @@
...
@@ -97,6 +99,49 @@
<div class=" text-lg font-semibold mb-3">{$i18n.t('Models')}</div>
<div class=" text-lg font-semibold mb-3">{$i18n.t('Models')}</div>
<div class=" flex w-full space-x-2">
<div class="flex flex-1">
<div class=" self-center ml-1 mr-3">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z"
clip-rule="evenodd"
/>
</svg>
</div>
<input
class=" w-full text-sm pr-4 py-1 rounded-r-xl outline-none bg-transparent"
bind:value={searchValue}
placeholder={$i18n.t('Search Models')}
/>
</div>
<div>
<a
class=" px-2 py-2 rounded-xl border border-gray-200 dark:border-gray-600 dark:border-0 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 transition font-medium text-sm flex items-center space-x-1"
href="/workspace/models/create"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z"
/>
</svg>
</a>
</div>
</div>
<hr class=" dark:border-gray-850 my-2.5" />
<a class=" flex space-x-4 cursor-pointer w-full mb-2 px-3 py-2" href="/workspace/models/create">
<a class=" flex space-x-4 cursor-pointer w-full mb-2 px-3 py-2" href="/workspace/models/create">
<div class=" self-center w-10">
<div class=" self-center w-10">
<div
<div
...
@@ -121,7 +166,9 @@
...
@@ -121,7 +166,9 @@
<hr class=" dark:border-gray-850" />
<hr class=" dark:border-gray-850" />
<div class=" my-2 mb-5">
<div class=" my-2 mb-5">
{#each $models as model}
{#each $models.filter((m) => searchValue === '' || m.name
.toLowerCase()
.includes(searchValue.toLowerCase())) as model}
<div
<div
class=" flex space-x-4 cursor-pointer w-full px-3 py-2 dark:hover:bg-white/5 hover:bg-black/5 rounded-xl"
class=" flex space-x-4 cursor-pointer w-full px-3 py-2 dark:hover:bg-white/5 hover:bg-black/5 rounded-xl"
>
>
...
...
src/lib/components/workspace/Playground.svelte
View file @
276b7b90
...
@@ -5,12 +5,7 @@
...
@@ -5,12 +5,7 @@
import { toast } from 'svelte-sonner';
import { toast } from 'svelte-sonner';
import {
import { OLLAMA_API_BASE_URL, OPENAI_API_BASE_URL, WEBUI_API_BASE_URL } from '$lib/constants';
LITELLM_API_BASE_URL,
OLLAMA_API_BASE_URL,
OPENAI_API_BASE_URL,
WEBUI_API_BASE_URL
} from '$lib/constants';
import { WEBUI_NAME, config, user, models, settings } from '$lib/stores';
import { WEBUI_NAME, config, user, models, settings } from '$lib/stores';
import { cancelOllamaRequest, generateChatCompletion } from '$lib/apis/ollama';
import { cancelOllamaRequest, generateChatCompletion } from '$lib/apis/ollama';
...
@@ -79,11 +74,7 @@
...
@@ -79,11 +74,7 @@
}
}
]
]
},
},
model.external
model?.owned_by === 'openai' ? `${OPENAI_API_BASE_URL}` : `${OLLAMA_API_BASE_URL}/v1`
? model.source === 'litellm'
? `${LITELLM_API_BASE_URL}/v1`
: `${OPENAI_API_BASE_URL}`
: `${OLLAMA_API_BASE_URL}/v1`
);
);
if (res && res.ok) {
if (res && res.ok) {
...
@@ -150,11 +141,7 @@
...
@@ -150,11 +141,7 @@
...messages
...messages
].filter((message) => message)
].filter((message) => message)
},
},
model.external
model?.owned_by === 'openai' ? `${OPENAI_API_BASE_URL}` : `${OLLAMA_API_BASE_URL}/v1`
? model.source === 'litellm'
? `${LITELLM_API_BASE_URL}/v1`
: `${OPENAI_API_BASE_URL}`
: `${OLLAMA_API_BASE_URL}/v1`
);
);
let responseMessage;
let responseMessage;
...
...
Prev
1
2
3
4
5
6
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