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
c255cba1
"vscode:/vscode.git/clone" did not exist on "41c51d07c54a420302e8cd507a6426b1ada73251"
Commit
c255cba1
authored
Mar 06, 2024
by
Timothy J. Baek
Browse files
feat: multiple openai apis
parent
51d509ba
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
270 additions
and
136 deletions
+270
-136
backend/apps/openai/main.py
backend/apps/openai/main.py
+174
-83
backend/config.py
backend/config.py
+1
-1
backend/constants.py
backend/constants.py
+2
-0
src/lib/apis/openai/index.ts
src/lib/apis/openai/index.ts
+14
-14
src/lib/components/chat/Settings/Connections.svelte
src/lib/components/chat/Settings/Connections.svelte
+79
-35
src/routes/(app)/+layout.svelte
src/routes/(app)/+layout.svelte
+0
-3
No files found.
backend/apps/openai/main.py
View file @
c255cba1
...
@@ -3,7 +3,10 @@ from fastapi.middleware.cors import CORSMiddleware
...
@@ -3,7 +3,10 @@ from fastapi.middleware.cors import CORSMiddleware
from
fastapi.responses
import
StreamingResponse
,
JSONResponse
,
FileResponse
from
fastapi.responses
import
StreamingResponse
,
JSONResponse
,
FileResponse
import
requests
import
requests
import
aiohttp
import
asyncio
import
json
import
json
from
pydantic
import
BaseModel
from
pydantic
import
BaseModel
...
@@ -15,7 +18,9 @@ from utils.utils import (
...
@@ -15,7 +18,9 @@ from utils.utils import (
get_verified_user
,
get_verified_user
,
get_admin_user
,
get_admin_user
,
)
)
from
config
import
OPENAI_API_BASE_URL
,
OPENAI_API_KEY
,
CACHE_DIR
from
config
import
OPENAI_API_BASE_URLS
,
OPENAI_API_KEYS
,
CACHE_DIR
from
typing
import
List
,
Optional
import
hashlib
import
hashlib
from
pathlib
import
Path
from
pathlib
import
Path
...
@@ -29,116 +34,208 @@ app.add_middleware(
...
@@ -29,116 +34,208 @@ app.add_middleware(
allow_headers
=
[
"*"
],
allow_headers
=
[
"*"
],
)
)
app
.
state
.
OPENAI_API_BASE_URL
=
OPENAI_API_BASE_URL
app
.
state
.
OPENAI_API_BASE_URLS
=
OPENAI_API_BASE_URLS
app
.
state
.
OPENAI_API_KEY
=
OPENAI_API_KEY
app
.
state
.
OPENAI_API_KEYS
=
OPENAI_API_KEYS
app
.
state
.
MODELS
=
{}
@
app
.
middleware
(
"http"
)
async
def
check_url
(
request
:
Request
,
call_next
):
if
len
(
app
.
state
.
MODELS
)
==
0
:
await
get_all_models
()
else
:
pass
class
UrlUpdateForm
(
BaseModel
):
response
=
await
call_next
(
request
)
url
:
str
return
response
class
Key
UpdateForm
(
BaseModel
):
class
Urls
UpdateForm
(
BaseModel
):
key
:
str
urls
:
List
[
str
]
@
app
.
get
(
"/url"
)
class
KeysUpdateForm
(
BaseModel
):
async
def
get_openai_url
(
user
=
Depends
(
get_admin_user
)):
keys
:
List
[
str
]
return
{
"OPENAI_API_BASE_URL"
:
app
.
state
.
OPENAI_API_BASE_URL
}
@
app
.
post
(
"/url/update"
)
@
app
.
get
(
"/urls"
)
async
def
update_openai_url
(
form_data
:
UrlUpdateForm
,
user
=
Depends
(
get_admin_user
)):
async
def
get_openai_urls
(
user
=
Depends
(
get_admin_user
)):
app
.
state
.
OPENAI_API_BASE_URL
=
form_data
.
url
return
{
"OPENAI_API_BASE_URLS"
:
app
.
state
.
OPENAI_API_BASE_URLS
}
return
{
"OPENAI_API_BASE_URL"
:
app
.
state
.
OPENAI_API_BASE_URL
}
@
app
.
get
(
"/key"
)
@
app
.
post
(
"/urls/update"
)
async
def
get_openai_key
(
user
=
Depends
(
get_admin_user
)):
async
def
update_openai_urls
(
form_data
:
UrlsUpdateForm
,
user
=
Depends
(
get_admin_user
)):
return
{
"OPENAI_API_KEY"
:
app
.
state
.
OPENAI_API_KEY
}
app
.
state
.
OPENAI_API_BASE_URLS
=
form_data
.
urls
return
{
"OPENAI_API_BASE_URLS"
:
app
.
state
.
OPENAI_API_BASE_URLS
}
@
app
.
post
(
"/key/update"
)
@
app
.
get
(
"/keys"
)
async
def
update_openai_key
(
form_data
:
KeyUpdateForm
,
user
=
Depends
(
get_admin_user
)):
async
def
get_openai_keys
(
user
=
Depends
(
get_admin_user
)):
app
.
state
.
OPENAI_API_KEY
=
form_data
.
key
return
{
"OPENAI_API_KEYS"
:
app
.
state
.
OPENAI_API_KEYS
}
return
{
"OPENAI_API_KEY"
:
app
.
state
.
OPENAI_API_KEY
}
@
app
.
post
(
"/keys/update"
)
async
def
update_openai_key
(
form_data
:
KeysUpdateForm
,
user
=
Depends
(
get_admin_user
)):
app
.
state
.
OPENAI_API_KEYS
=
form_data
.
keys
return
{
"OPENAI_API_KEYS"
:
app
.
state
.
OPENAI_API_KEYS
}
@
app
.
post
(
"/audio/speech"
)
@
app
.
post
(
"/audio/speech"
)
async
def
speech
(
request
:
Request
,
user
=
Depends
(
get_verified_user
)):
async
def
speech
(
request
:
Request
,
user
=
Depends
(
get_verified_user
)):
target_url
=
f
"
{
app
.
state
.
OPENAI_API_BASE_URL
}
/audio/speech"
idx
=
None
try
:
idx
=
app
.
state
.
OPENAI_API_BASE_URLS
.
index
(
"https://api.openai.com/v1"
)
body
=
await
request
.
body
()
name
=
hashlib
.
sha256
(
body
).
hexdigest
()
SPEECH_CACHE_DIR
=
Path
(
CACHE_DIR
).
joinpath
(
"./audio/speech/"
)
SPEECH_CACHE_DIR
.
mkdir
(
parents
=
True
,
exist_ok
=
True
)
file_path
=
SPEECH_CACHE_DIR
.
joinpath
(
f
"
{
name
}
.mp3"
)
file_body_path
=
SPEECH_CACHE_DIR
.
joinpath
(
f
"
{
name
}
.json"
)
# Check if the file already exists in the cache
if
file_path
.
is_file
():
return
FileResponse
(
file_path
)
headers
=
{}
headers
[
"Authorization"
]
=
f
"Bearer
{
app
.
state
.
OPENAI_API_KEYS
[
idx
]
}
"
headers
[
"Content-Type"
]
=
"application/json"
try
:
r
=
requests
.
post
(
url
=
f
"
{
app
.
state
.
OPENAI_API_BASE_URLS
[
idx
]
}
/audio/speech"
,
data
=
body
,
headers
=
headers
,
stream
=
True
,
)
if
app
.
state
.
OPENAI_API_KEY
==
""
:
r
.
raise_for_status
()
raise
HTTPException
(
status_code
=
401
,
detail
=
ERROR_MESSAGES
.
API_KEY_NOT_FOUND
)
body
=
await
request
.
body
()
# Save the streaming content to a file
with
open
(
file_path
,
"wb"
)
as
f
:
for
chunk
in
r
.
iter_content
(
chunk_size
=
8192
):
f
.
write
(
chunk
)
name
=
hashlib
.
sha256
(
body
).
hexdigest
()
with
open
(
file_body_path
,
"w"
)
as
f
:
json
.
dump
(
json
.
loads
(
body
.
decode
(
"utf-8"
)),
f
)
SPEECH_CACHE_DIR
=
Path
(
CACHE_DIR
).
joinpath
(
"./audio/speech/"
)
# Return the saved file
SPEECH_CACHE_DIR
.
mkdir
(
parents
=
True
,
exist_ok
=
True
)
return
FileResponse
(
file_path
)
file_path
=
SPEECH_CACHE_DIR
.
joinpath
(
f
"
{
name
}
.mp3"
)
file_body_path
=
SPEECH_CACHE_DIR
.
joinpath
(
f
"
{
name
}
.json"
)
# Check if the file already exists in the cache
except
Exception
as
e
:
if
file_path
.
is_file
():
print
(
e
)
return
FileResponse
(
file_path
)
error_detail
=
"Open WebUI: Server Connection Error"
if
r
is
not
None
:
try
:
res
=
r
.
json
()
if
"error"
in
res
:
error_detail
=
f
"External:
{
res
[
'error'
]
}
"
except
:
error_detail
=
f
"External:
{
e
}
"
raise
HTTPException
(
status_code
=
r
.
status_code
,
detail
=
error_detail
)
except
ValueError
:
raise
HTTPException
(
status_code
=
401
,
detail
=
ERROR_MESSAGES
.
OPENAI_NOT_FOUND
)
headers
=
{}
headers
[
"Authorization"
]
=
f
"Bearer
{
app
.
state
.
OPENAI_API_KEY
}
"
headers
[
"Content-Type"
]
=
"application/json"
async
def
fetch_url
(
url
,
key
):
try
:
try
:
print
(
"openai"
)
headers
=
{
"Authorization"
:
f
"Bearer
{
key
}
"
}
r
=
requests
.
post
(
async
with
aiohttp
.
ClientSession
()
as
session
:
url
=
target_url
,
async
with
session
.
get
(
url
,
headers
=
headers
)
as
response
:
data
=
body
,
return
await
response
.
json
()
headers
=
headers
,
except
Exception
as
e
:
stream
=
True
,
# Handle connection error here
print
(
f
"Connection error:
{
e
}
"
)
return
None
def
merge_models_lists
(
model_lists
):
merged_list
=
[]
for
idx
,
models
in
enumerate
(
model_lists
):
merged_list
.
extend
(
[
{
**
model
,
"urlIdx"
:
idx
}
for
model
in
models
if
"api.openai.com"
not
in
app
.
state
.
OPENAI_API_BASE_URLS
[
idx
]
or
"gpt"
in
model
[
"id"
]
]
)
)
r
.
raise_for_status
()
r
eturn
merged_list
# Save the streaming content to a file
with
open
(
file_path
,
"wb"
)
as
f
:
for
chunk
in
r
.
iter_content
(
chunk_size
=
8192
):
f
.
write
(
chunk
)
with
open
(
file_body_path
,
"w"
)
as
f
:
async
def
get_all_models
():
json
.
dump
(
json
.
loads
(
body
.
decode
(
"utf-8"
)),
f
)
print
(
"get_all_models"
)
tasks
=
[
fetch_url
(
f
"
{
url
}
/models"
,
app
.
state
.
OPENAI_API_KEYS
[
idx
])
for
idx
,
url
in
enumerate
(
app
.
state
.
OPENAI_API_BASE_URLS
)
]
responses
=
await
asyncio
.
gather
(
*
tasks
)
responses
=
list
(
filter
(
lambda
x
:
x
is
not
None
,
responses
))
# Return the saved file
models
=
{
return
FileResponse
(
file_path
)
"data"
:
merge_models_lists
(
list
(
map
(
lambda
response
:
response
[
"data"
],
responses
))
)
}
app
.
state
.
MODELS
=
{
model
[
"id"
]:
model
for
model
in
models
[
"data"
]}
except
Exception
as
e
:
return
models
print
(
e
)
error_detail
=
"Open WebUI: Server Connection Error"
if
r
is
not
None
:
try
:
res
=
r
.
json
()
if
"error"
in
res
:
error_detail
=
f
"External:
{
res
[
'error'
]
}
"
except
:
error_detail
=
f
"External:
{
e
}
"
raise
HTTPException
(
status_code
=
r
.
status_code
,
detail
=
error_detail
)
# , user=Depends(get_current_user)
@
app
.
get
(
"/models"
)
@
app
.
get
(
"/models/{url_idx}"
)
async
def
get_models
(
url_idx
:
Optional
[
int
]
=
None
):
if
url_idx
==
None
:
return
await
get_all_models
()
else
:
url
=
app
.
state
.
OPENAI_API_BASE_URLS
[
url_idx
]
try
:
r
=
requests
.
request
(
method
=
"GET"
,
url
=
f
"
{
url
}
/models"
)
r
.
raise_for_status
()
response_data
=
r
.
json
()
if
"api.openai.com"
in
url
:
response_data
[
"data"
]
=
list
(
filter
(
lambda
model
:
"gpt"
in
model
[
"id"
],
response_data
[
"data"
])
)
return
response_data
except
Exception
as
e
:
print
(
e
)
error_detail
=
"Open WebUI: Server Connection Error"
if
r
is
not
None
:
try
:
res
=
r
.
json
()
if
"error"
in
res
:
error_detail
=
f
"External:
{
res
[
'error'
]
}
"
except
:
error_detail
=
f
"External:
{
e
}
"
raise
HTTPException
(
status_code
=
r
.
status_code
if
r
else
500
,
detail
=
error_detail
,
)
@
app
.
api_route
(
"/{path:path}"
,
methods
=
[
"GET"
,
"POST"
,
"PUT"
,
"DELETE"
])
@
app
.
api_route
(
"/{path:path}"
,
methods
=
[
"GET"
,
"POST"
,
"PUT"
,
"DELETE"
])
async
def
proxy
(
path
:
str
,
request
:
Request
,
user
=
Depends
(
get_verified_user
)):
async
def
proxy
(
path
:
str
,
request
:
Request
,
user
=
Depends
(
get_verified_user
)):
target_url
=
f
"
{
app
.
state
.
OPENAI_API_BASE_URL
}
/
{
path
}
"
idx
=
0
print
(
target_url
,
app
.
state
.
OPENAI_API_KEY
)
if
app
.
state
.
OPENAI_API_KEY
==
""
:
raise
HTTPException
(
status_code
=
401
,
detail
=
ERROR_MESSAGES
.
API_KEY_NOT_FOUND
)
body
=
await
request
.
body
()
body
=
await
request
.
body
()
# TODO: Remove below after gpt-4-vision fix from Open AI
# TODO: Remove below after gpt-4-vision fix from Open AI
# Try to decode the body of the request from bytes to a UTF-8 string (Require add max_token to fix gpt-4-vision)
# Try to decode the body of the request from bytes to a UTF-8 string (Require add max_token to fix gpt-4-vision)
try
:
try
:
body
=
body
.
decode
(
"utf-8"
)
body
=
body
.
decode
(
"utf-8"
)
body
=
json
.
loads
(
body
)
body
=
json
.
loads
(
body
)
idx
=
app
.
state
.
MODELS
[
body
.
get
(
"model"
)][
"urlIdx"
]
# Check if the model is "gpt-4-vision-preview" and set "max_tokens" to 4000
# Check if the model is "gpt-4-vision-preview" and set "max_tokens" to 4000
# This is a workaround until OpenAI fixes the issue with this model
# This is a workaround until OpenAI fixes the issue with this model
if
body
.
get
(
"model"
)
==
"gpt-4-vision-preview"
:
if
body
.
get
(
"model"
)
==
"gpt-4-vision-preview"
:
...
@@ -158,8 +255,16 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)):
...
@@ -158,8 +255,16 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)):
except
json
.
JSONDecodeError
as
e
:
except
json
.
JSONDecodeError
as
e
:
print
(
"Error loading request body into a dictionary:"
,
e
)
print
(
"Error loading request body into a dictionary:"
,
e
)
url
=
app
.
state
.
OPENAI_API_BASE_URLS
[
idx
]
key
=
app
.
state
.
OPENAI_API_KEYS
[
idx
]
target_url
=
f
"
{
url
}
/
{
path
}
"
if
key
==
""
:
raise
HTTPException
(
status_code
=
401
,
detail
=
ERROR_MESSAGES
.
API_KEY_NOT_FOUND
)
headers
=
{}
headers
=
{}
headers
[
"Authorization"
]
=
f
"Bearer
{
app
.
state
.
OPENAI_API_KEY
}
"
headers
[
"Authorization"
]
=
f
"Bearer
{
key
}
"
headers
[
"Content-Type"
]
=
"application/json"
headers
[
"Content-Type"
]
=
"application/json"
try
:
try
:
...
@@ -181,21 +286,7 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)):
...
@@ -181,21 +286,7 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)):
headers
=
dict
(
r
.
headers
),
headers
=
dict
(
r
.
headers
),
)
)
else
:
else
:
# For non-SSE, read the response and return it
# response_data = (
# r.json()
# if r.headers.get("Content-Type", "")
# == "application/json"
# else r.text
# )
response_data
=
r
.
json
()
response_data
=
r
.
json
()
if
"api.openai.com"
in
app
.
state
.
OPENAI_API_BASE_URL
and
path
==
"models"
:
response_data
[
"data"
]
=
list
(
filter
(
lambda
model
:
"gpt"
in
model
[
"id"
],
response_data
[
"data"
])
)
return
response_data
return
response_data
except
Exception
as
e
:
except
Exception
as
e
:
print
(
e
)
print
(
e
)
...
...
backend/config.py
View file @
c255cba1
...
@@ -240,7 +240,7 @@ if OPENAI_API_BASE_URL == "":
...
@@ -240,7 +240,7 @@ if OPENAI_API_BASE_URL == "":
OPENAI_API_KEYS
=
os
.
environ
.
get
(
"OPENAI_API_KEYS"
,
""
)
OPENAI_API_KEYS
=
os
.
environ
.
get
(
"OPENAI_API_KEYS"
,
""
)
OPENAI_API_KEYS
=
OPENAI_API_KEYS
if
OPENAI_API_KEYS
!=
""
else
OPENAI_API_KEY
OPENAI_API_KEYS
=
OPENAI_API_KEYS
if
OPENAI_API_KEYS
!=
""
else
OPENAI_API_KEY
OPENAI_API_KEYS
=
[
url
.
strip
()
for
url
in
OPENAI_API_
BASE_URL
.
split
(
";"
)]
OPENAI_API_KEYS
=
[
url
.
strip
()
for
url
in
OPENAI_API_
KEYS
.
split
(
";"
)]
OPENAI_API_BASE_URLS
=
os
.
environ
.
get
(
"OPENAI_API_BASE_URLS"
,
""
)
OPENAI_API_BASE_URLS
=
os
.
environ
.
get
(
"OPENAI_API_BASE_URLS"
,
""
)
...
...
backend/constants.py
View file @
c255cba1
...
@@ -41,6 +41,7 @@ class ERROR_MESSAGES(str, Enum):
...
@@ -41,6 +41,7 @@ class ERROR_MESSAGES(str, Enum):
NOT_FOUND
=
"We could not find what you're looking for :/"
NOT_FOUND
=
"We could not find what you're looking for :/"
USER_NOT_FOUND
=
"We could not find what you're looking for :/"
USER_NOT_FOUND
=
"We could not find what you're looking for :/"
API_KEY_NOT_FOUND
=
"Oops! It looks like there's a hiccup. The API key is missing. Please make sure to provide a valid API key to access this feature."
API_KEY_NOT_FOUND
=
"Oops! It looks like there's a hiccup. The API key is missing. Please make sure to provide a valid API key to access this feature."
MALICIOUS
=
"Unusual activities detected, please try again in a few minutes."
MALICIOUS
=
"Unusual activities detected, please try again in a few minutes."
PANDOC_NOT_INSTALLED
=
"Pandoc is not installed on the server. Please contact your administrator for assistance."
PANDOC_NOT_INSTALLED
=
"Pandoc is not installed on the server. Please contact your administrator for assistance."
...
@@ -50,3 +51,4 @@ class ERROR_MESSAGES(str, Enum):
...
@@ -50,3 +51,4 @@ class ERROR_MESSAGES(str, Enum):
RATE_LIMIT_EXCEEDED
=
"API rate limit exceeded"
RATE_LIMIT_EXCEEDED
=
"API rate limit exceeded"
MODEL_NOT_FOUND
=
lambda
name
=
""
:
f
"Model '
{
name
}
' was not found"
MODEL_NOT_FOUND
=
lambda
name
=
""
:
f
"Model '
{
name
}
' was not found"
OPENAI_NOT_FOUND
=
lambda
name
=
""
:
f
"OpenAI API was not found"
src/lib/apis/openai/index.ts
View file @
c255cba1
import
{
OPENAI_API_BASE_URL
}
from
'
$lib/constants
'
;
import
{
OPENAI_API_BASE_URL
}
from
'
$lib/constants
'
;
export
const
getOpenAIUrl
=
async
(
token
:
string
=
''
)
=>
{
export
const
getOpenAIUrl
s
=
async
(
token
:
string
=
''
)
=>
{
let
error
=
null
;
let
error
=
null
;
const
res
=
await
fetch
(
`
${
OPENAI_API_BASE_URL
}
/url`
,
{
const
res
=
await
fetch
(
`
${
OPENAI_API_BASE_URL
}
/url
s
`
,
{
method
:
'
GET
'
,
method
:
'
GET
'
,
headers
:
{
headers
:
{
Accept
:
'
application/json
'
,
Accept
:
'
application/json
'
,
...
@@ -29,13 +29,13 @@ export const getOpenAIUrl = async (token: string = '') => {
...
@@ -29,13 +29,13 @@ export const getOpenAIUrl = async (token: string = '') => {
throw
error
;
throw
error
;
}
}
return
res
.
OPENAI_API_BASE_URL
;
return
res
.
OPENAI_API_BASE_URL
S
;
};
};
export
const
updateOpenAIUrl
=
async
(
token
:
string
=
''
,
url
:
string
)
=>
{
export
const
updateOpenAIUrl
s
=
async
(
token
:
string
=
''
,
url
s
:
string
[]
)
=>
{
let
error
=
null
;
let
error
=
null
;
const
res
=
await
fetch
(
`
${
OPENAI_API_BASE_URL
}
/url/update`
,
{
const
res
=
await
fetch
(
`
${
OPENAI_API_BASE_URL
}
/url
s
/update`
,
{
method
:
'
POST
'
,
method
:
'
POST
'
,
headers
:
{
headers
:
{
Accept
:
'
application/json
'
,
Accept
:
'
application/json
'
,
...
@@ -43,7 +43,7 @@ export const updateOpenAIUrl = async (token: string = '', url: string) => {
...
@@ -43,7 +43,7 @@ export const updateOpenAIUrl = async (token: string = '', url: string) => {
...(
token
&&
{
authorization
:
`Bearer
${
token
}
`
})
...(
token
&&
{
authorization
:
`Bearer
${
token
}
`
})
},
},
body
:
JSON
.
stringify
({
body
:
JSON
.
stringify
({
url
:
url
url
s
:
url
s
})
})
})
})
.
then
(
async
(
res
)
=>
{
.
then
(
async
(
res
)
=>
{
...
@@ -64,13 +64,13 @@ export const updateOpenAIUrl = async (token: string = '', url: string) => {
...
@@ -64,13 +64,13 @@ export const updateOpenAIUrl = async (token: string = '', url: string) => {
throw
error
;
throw
error
;
}
}
return
res
.
OPENAI_API_BASE_URL
;
return
res
.
OPENAI_API_BASE_URL
S
;
};
};
export
const
getOpenAIKey
=
async
(
token
:
string
=
''
)
=>
{
export
const
getOpenAIKey
s
=
async
(
token
:
string
=
''
)
=>
{
let
error
=
null
;
let
error
=
null
;
const
res
=
await
fetch
(
`
${
OPENAI_API_BASE_URL
}
/key`
,
{
const
res
=
await
fetch
(
`
${
OPENAI_API_BASE_URL
}
/key
s
`
,
{
method
:
'
GET
'
,
method
:
'
GET
'
,
headers
:
{
headers
:
{
Accept
:
'
application/json
'
,
Accept
:
'
application/json
'
,
...
@@ -96,13 +96,13 @@ export const getOpenAIKey = async (token: string = '') => {
...
@@ -96,13 +96,13 @@ export const getOpenAIKey = async (token: string = '') => {
throw
error
;
throw
error
;
}
}
return
res
.
OPENAI_API_KEY
;
return
res
.
OPENAI_API_KEY
S
;
};
};
export
const
updateOpenAIKey
=
async
(
token
:
string
=
''
,
key
:
string
)
=>
{
export
const
updateOpenAIKey
s
=
async
(
token
:
string
=
''
,
key
s
:
string
[]
)
=>
{
let
error
=
null
;
let
error
=
null
;
const
res
=
await
fetch
(
`
${
OPENAI_API_BASE_URL
}
/key/update`
,
{
const
res
=
await
fetch
(
`
${
OPENAI_API_BASE_URL
}
/key
s
/update`
,
{
method
:
'
POST
'
,
method
:
'
POST
'
,
headers
:
{
headers
:
{
Accept
:
'
application/json
'
,
Accept
:
'
application/json
'
,
...
@@ -110,7 +110,7 @@ export const updateOpenAIKey = async (token: string = '', key: string) => {
...
@@ -110,7 +110,7 @@ export const updateOpenAIKey = async (token: string = '', key: string) => {
...(
token
&&
{
authorization
:
`Bearer
${
token
}
`
})
...(
token
&&
{
authorization
:
`Bearer
${
token
}
`
})
},
},
body
:
JSON
.
stringify
({
body
:
JSON
.
stringify
({
key
:
key
key
s
:
key
s
})
})
})
})
.
then
(
async
(
res
)
=>
{
.
then
(
async
(
res
)
=>
{
...
@@ -131,7 +131,7 @@ export const updateOpenAIKey = async (token: string = '', key: string) => {
...
@@ -131,7 +131,7 @@ export const updateOpenAIKey = async (token: string = '', key: string) => {
throw
error
;
throw
error
;
}
}
return
res
.
OPENAI_API_KEY
;
return
res
.
OPENAI_API_KEY
S
;
};
};
export
const
getOpenAIModels
=
async
(
token
:
string
=
''
)
=>
{
export
const
getOpenAIModels
=
async
(
token
:
string
=
''
)
=>
{
...
...
src/lib/components/chat/Settings/Connections.svelte
View file @
c255cba1
...
@@ -4,7 +4,12 @@
...
@@ -4,7 +4,12 @@
const dispatch = createEventDispatcher();
const dispatch = createEventDispatcher();
import { getOllamaUrls, getOllamaVersion, updateOllamaUrls } from '$lib/apis/ollama';
import { getOllamaUrls, getOllamaVersion, updateOllamaUrls } from '$lib/apis/ollama';
import { getOpenAIKey, getOpenAIUrl, updateOpenAIKey, updateOpenAIUrl } from '$lib/apis/openai';
import {
getOpenAIKeys,
getOpenAIUrls,
updateOpenAIKeys,
updateOpenAIUrls
} from '$lib/apis/openai';
import { toast } from 'svelte-sonner';
import { toast } from 'svelte-sonner';
export let getModels: Function;
export let getModels: Function;
...
@@ -16,12 +21,14 @@
...
@@ -16,12 +21,14 @@
let OPENAI_API_KEY = '';
let OPENAI_API_KEY = '';
let OPENAI_API_BASE_URL = '';
let OPENAI_API_BASE_URL = '';
let OPENAI_API_KEYS = [''];
let OPENAI_API_BASE_URLS = [''];
let showOpenAI = false;
let showOpenAI = false;
let showLiteLLM = false;
const updateOpenAIHandler = async () => {
const updateOpenAIHandler = async () => {
OPENAI_API_BASE_URL = await updateOpenAIUrl(localStorage.token, OPENAI_API_BASE_URL);
OPENAI_API_BASE_URL
S
= await updateOpenAIUrl
s
(localStorage.token, OPENAI_API_BASE_URL
S
);
OPENAI_API_KEY = await updateOpenAIKey(localStorage.token, OPENAI_API_KEY);
OPENAI_API_KEY
S
= await updateOpenAIKey
s
(localStorage.token, OPENAI_API_KEY
S
);
await models.set(await getModels());
await models.set(await getModels());
};
};
...
@@ -43,8 +50,8 @@
...
@@ -43,8 +50,8 @@
onMount(async () => {
onMount(async () => {
if ($user.role === 'admin') {
if ($user.role === 'admin') {
OLLAMA_BASE_URLS = await getOllamaUrls(localStorage.token);
OLLAMA_BASE_URLS = await getOllamaUrls(localStorage.token);
OPENAI_API_BASE_URL = await getOpenAIUrl(localStorage.token);
OPENAI_API_BASE_URL
S
= await getOpenAIUrl
s
(localStorage.token);
OPENAI_API_KEY = await getOpenAIKey(localStorage.token);
OPENAI_API_KEY
S
= await getOpenAIKey
s
(localStorage.token);
}
}
});
});
</script>
</script>
...
@@ -71,37 +78,74 @@
...
@@ -71,37 +78,74 @@
</div>
</div>
{#if showOpenAI}
{#if showOpenAI}
<div>
<div class="flex flex-col gap-1">
<div class=" mb-2.5 text-sm font-medium">API Key</div>
{#each OPENAI_API_BASE_URLS as url, idx}
<div class="flex w-full">
<div class="flex w-full gap-2">
<div class="flex-1">
<div class="flex-1">
<input
<input
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder="Enter OpenAI API Key"
placeholder="API Base URL"
bind:value={OPENAI_API_KEY}
bind:value={url}
autocomplete="off"
autocomplete="off"
/>
/>
</div>
</div>
</div>
</div>
<div>
<div class="flex-1">
<div class=" mb-2.5 text-sm font-medium">API Base URL</div>
<input
<div class="flex w-full">
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
<div class="flex-1">
placeholder="API Key"
<input
bind:value={OPENAI_API_KEYS[idx]}
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
autocomplete="off"
placeholder="Enter OpenAI API Base URL"
/>
bind:value={OPENAI_API_BASE_URL}
</div>
autocomplete="off"
<div class="self-center flex items-center">
/>
{#if idx === 0}
<button
class="px-1"
on:click={() => {
OPENAI_API_BASE_URLS = [...OPENAI_API_BASE_URLS, ''];
OPENAI_API_KEYS = [...OPENAI_API_KEYS, ''];
}}
type="button"
>
<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>
</button>
{:else}
<button
class="px-1"
on:click={() => {
OPENAI_API_BASE_URLS = OPENAI_API_BASE_URLS.filter(
(url, urlIdx) => idx !== urlIdx
);
OPENAI_API_KEYS = OPENAI_API_KEYS.filter((key, keyIdx) => idx !== keyIdx);
}}
type="button"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path d="M3.75 7.25a.75.75 0 0 0 0 1.5h8.5a.75.75 0 0 0 0-1.5h-8.5Z" />
</svg>
</button>
{/if}
</div>
</div>
</div>
</div>
<div class=" mb-1 text-xs text-gray-400 dark:text-gray-500">
<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
WebUI will make requests to <span class=" text-gray-200">'{url}/models'</span>
WebUI will make requests to <span class=" text-gray-200"
</div>
>'{OPENAI_API_BASE_URL}/chat'</span
{/each}
>
</div>
</div>
</div>
{/if}
{/if}
</div>
</div>
...
...
src/routes/(app)/+layout.svelte
View file @
c255cba1
...
@@ -97,14 +97,11 @@
...
@@ -97,14 +97,11 @@
if (localDBChats.length === 0) {
if (localDBChats.length === 0) {
await deleteDB('Chats');
await deleteDB('Chats');
}
}
console.log('localdb', localDBChats);
}
}
console.log(DB);
console.log(DB);
} catch (error) {
} catch (error) {
// IndexedDB Not Found
// IndexedDB Not Found
console.log('IDB Not Found');
}
}
console.log();
console.log();
...
...
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