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
1eebb85f
Unverified
Commit
1eebb85f
authored
Jun 27, 2024
by
Timothy Jaeryang Baek
Committed by
GitHub
Jun 27, 2024
Browse files
Merge pull request #3323 from open-webui/dev
0.3.6
parents
9e4dd4b8
b224ba00
Changes
177
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
1935 additions
and
47 deletions
+1935
-47
backend/apps/webui/internal/migrations/016_add_valves_and_is_active.py
...webui/internal/migrations/016_add_valves_and_is_active.py
+50
-0
backend/apps/webui/internal/migrations/017_add_user_oauth_sub.py
.../apps/webui/internal/migrations/017_add_user_oauth_sub.py
+49
-0
backend/apps/webui/internal/migrations/018_add_function_is_global.py
...s/webui/internal/migrations/018_add_function_is_global.py
+49
-0
backend/apps/webui/internal/wrappers.py
backend/apps/webui/internal/wrappers.py
+72
-0
backend/apps/webui/main.py
backend/apps/webui/main.py
+247
-3
backend/apps/webui/models/auths.py
backend/apps/webui/models/auths.py
+4
-1
backend/apps/webui/models/files.py
backend/apps/webui/models/files.py
+112
-0
backend/apps/webui/models/functions.py
backend/apps/webui/models/functions.py
+261
-0
backend/apps/webui/models/tools.py
backend/apps/webui/models/tools.py
+72
-0
backend/apps/webui/models/users.py
backend/apps/webui/models/users.py
+25
-0
backend/apps/webui/routers/auths.py
backend/apps/webui/routers/auths.py
+32
-4
backend/apps/webui/routers/chats.py
backend/apps/webui/routers/chats.py
+22
-22
backend/apps/webui/routers/configs.py
backend/apps/webui/routers/configs.py
+2
-2
backend/apps/webui/routers/documents.py
backend/apps/webui/routers/documents.py
+4
-4
backend/apps/webui/routers/files.py
backend/apps/webui/routers/files.py
+242
-0
backend/apps/webui/routers/functions.py
backend/apps/webui/routers/functions.py
+423
-0
backend/apps/webui/routers/memories.py
backend/apps/webui/routers/memories.py
+2
-1
backend/apps/webui/routers/prompts.py
backend/apps/webui/routers/prompts.py
+3
-3
backend/apps/webui/routers/tools.py
backend/apps/webui/routers/tools.py
+197
-5
backend/apps/webui/utils.py
backend/apps/webui/utils.py
+67
-2
No files found.
backend/apps/webui/internal/migrations/016_add_valves_and_is_active.py
0 → 100644
View file @
1eebb85f
"""Peewee migrations -- 009_add_models.py.
Some examples (model - class or model name)::
> Model = migrator.orm['table_name'] # Return model in current state by name
> Model = migrator.ModelClass # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.run(func, *args, **kwargs) # Run python function with the given args
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
> migrator.change_fields(model, **fields) # Change fields
> migrator.remove_fields(model, *field_names, cascade=True)
> migrator.rename_field(model, old_field_name, new_field_name)
> migrator.rename_table(model, new_table_name)
> migrator.add_index(model, *col_names, unique=False)
> migrator.add_not_null(model, *field_names)
> migrator.add_default(model, field_name, default)
> migrator.add_constraint(model, name, sql)
> migrator.drop_index(model, *col_names)
> migrator.drop_not_null(model, *field_names)
> migrator.drop_constraints(model, *constraints)
"""
from
contextlib
import
suppress
import
peewee
as
pw
from
peewee_migrate
import
Migrator
with
suppress
(
ImportError
):
import
playhouse.postgres_ext
as
pw_pext
def
migrate
(
migrator
:
Migrator
,
database
:
pw
.
Database
,
*
,
fake
=
False
):
"""Write your migrations here."""
migrator
.
add_fields
(
"tool"
,
valves
=
pw
.
TextField
(
null
=
True
))
migrator
.
add_fields
(
"function"
,
valves
=
pw
.
TextField
(
null
=
True
))
migrator
.
add_fields
(
"function"
,
is_active
=
pw
.
BooleanField
(
default
=
False
))
def
rollback
(
migrator
:
Migrator
,
database
:
pw
.
Database
,
*
,
fake
=
False
):
"""Write your rollback migrations here."""
migrator
.
remove_fields
(
"tool"
,
"valves"
)
migrator
.
remove_fields
(
"function"
,
"valves"
)
migrator
.
remove_fields
(
"function"
,
"is_active"
)
backend/apps/webui/internal/migrations/017_add_user_oauth_sub.py
0 → 100644
View file @
1eebb85f
"""Peewee migrations -- 017_add_user_oauth_sub.py.
Some examples (model - class or model name)::
> Model = migrator.orm['table_name'] # Return model in current state by name
> Model = migrator.ModelClass # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.run(func, *args, **kwargs) # Run python function with the given args
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
> migrator.change_fields(model, **fields) # Change fields
> migrator.remove_fields(model, *field_names, cascade=True)
> migrator.rename_field(model, old_field_name, new_field_name)
> migrator.rename_table(model, new_table_name)
> migrator.add_index(model, *col_names, unique=False)
> migrator.add_not_null(model, *field_names)
> migrator.add_default(model, field_name, default)
> migrator.add_constraint(model, name, sql)
> migrator.drop_index(model, *col_names)
> migrator.drop_not_null(model, *field_names)
> migrator.drop_constraints(model, *constraints)
"""
from
contextlib
import
suppress
import
peewee
as
pw
from
peewee_migrate
import
Migrator
with
suppress
(
ImportError
):
import
playhouse.postgres_ext
as
pw_pext
def
migrate
(
migrator
:
Migrator
,
database
:
pw
.
Database
,
*
,
fake
=
False
):
"""Write your migrations here."""
migrator
.
add_fields
(
"user"
,
oauth_sub
=
pw
.
TextField
(
null
=
True
,
unique
=
True
),
)
def
rollback
(
migrator
:
Migrator
,
database
:
pw
.
Database
,
*
,
fake
=
False
):
"""Write your rollback migrations here."""
migrator
.
remove_fields
(
"user"
,
"oauth_sub"
)
backend/apps/webui/internal/migrations/018_add_function_is_global.py
0 → 100644
View file @
1eebb85f
"""Peewee migrations -- 017_add_user_oauth_sub.py.
Some examples (model - class or model name)::
> Model = migrator.orm['table_name'] # Return model in current state by name
> Model = migrator.ModelClass # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.run(func, *args, **kwargs) # Run python function with the given args
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
> migrator.change_fields(model, **fields) # Change fields
> migrator.remove_fields(model, *field_names, cascade=True)
> migrator.rename_field(model, old_field_name, new_field_name)
> migrator.rename_table(model, new_table_name)
> migrator.add_index(model, *col_names, unique=False)
> migrator.add_not_null(model, *field_names)
> migrator.add_default(model, field_name, default)
> migrator.add_constraint(model, name, sql)
> migrator.drop_index(model, *col_names)
> migrator.drop_not_null(model, *field_names)
> migrator.drop_constraints(model, *constraints)
"""
from
contextlib
import
suppress
import
peewee
as
pw
from
peewee_migrate
import
Migrator
with
suppress
(
ImportError
):
import
playhouse.postgres_ext
as
pw_pext
def
migrate
(
migrator
:
Migrator
,
database
:
pw
.
Database
,
*
,
fake
=
False
):
"""Write your migrations here."""
migrator
.
add_fields
(
"function"
,
is_global
=
pw
.
BooleanField
(
default
=
False
),
)
def
rollback
(
migrator
:
Migrator
,
database
:
pw
.
Database
,
*
,
fake
=
False
):
"""Write your rollback migrations here."""
migrator
.
remove_fields
(
"function"
,
"is_global"
)
backend/apps/webui/internal/wrappers.py
0 → 100644
View file @
1eebb85f
from
contextvars
import
ContextVar
from
peewee
import
*
from
peewee
import
PostgresqlDatabase
,
InterfaceError
as
PeeWeeInterfaceError
import
logging
from
playhouse.db_url
import
connect
,
parse
from
playhouse.shortcuts
import
ReconnectMixin
from
config
import
SRC_LOG_LEVELS
log
=
logging
.
getLogger
(
__name__
)
log
.
setLevel
(
SRC_LOG_LEVELS
[
"DB"
])
db_state_default
=
{
"closed"
:
None
,
"conn"
:
None
,
"ctx"
:
None
,
"transactions"
:
None
}
db_state
=
ContextVar
(
"db_state"
,
default
=
db_state_default
.
copy
())
class
PeeweeConnectionState
(
object
):
def
__init__
(
self
,
**
kwargs
):
super
().
__setattr__
(
"_state"
,
db_state
)
super
().
__init__
(
**
kwargs
)
def
__setattr__
(
self
,
name
,
value
):
self
.
_state
.
get
()[
name
]
=
value
def
__getattr__
(
self
,
name
):
value
=
self
.
_state
.
get
()[
name
]
return
value
class
CustomReconnectMixin
(
ReconnectMixin
):
reconnect_errors
=
(
# psycopg2
(
OperationalError
,
"termin"
),
(
InterfaceError
,
"closed"
),
# peewee
(
PeeWeeInterfaceError
,
"closed"
),
)
class
ReconnectingPostgresqlDatabase
(
CustomReconnectMixin
,
PostgresqlDatabase
):
pass
def
register_connection
(
db_url
):
db
=
connect
(
db_url
)
if
isinstance
(
db
,
PostgresqlDatabase
):
# Enable autoconnect for SQLite databases, managed by Peewee
db
.
autoconnect
=
True
db
.
reuse_if_open
=
True
log
.
info
(
"Connected to PostgreSQL database"
)
# Get the connection details
connection
=
parse
(
db_url
)
# Use our custom database class that supports reconnection
db
=
ReconnectingPostgresqlDatabase
(
connection
[
"database"
],
user
=
connection
[
"user"
],
password
=
connection
[
"password"
],
host
=
connection
[
"host"
],
port
=
connection
[
"port"
],
)
db
.
connect
(
reuse_if_open
=
True
)
elif
isinstance
(
db
,
SqliteDatabase
):
# Enable autoconnect for SQLite databases, managed by Peewee
db
.
autoconnect
=
True
db
.
reuse_if_open
=
True
log
.
info
(
"Connected to SQLite database"
)
else
:
raise
ValueError
(
"Unsupported database connection"
)
return
db
backend/apps/webui/main.py
View file @
1eebb85f
from
fastapi
import
FastAPI
,
Depends
from
fastapi.routing
import
APIRoute
from
fastapi.responses
import
StreamingResponse
from
fastapi.middleware.cors
import
CORSMiddleware
from
starlette.middleware.sessions
import
SessionMiddleware
from
apps.webui.routers
import
(
auths
,
users
,
...
...
@@ -12,7 +15,13 @@ from apps.webui.routers import (
configs
,
memories
,
utils
,
files
,
functions
,
)
from
apps.webui.models.functions
import
Functions
from
apps.webui.utils
import
load_function_module_by_id
from
utils.misc
import
stream_message_template
from
config
import
(
WEBUI_BUILD_HASH
,
SHOW_ADMIN_DETAILS
,
...
...
@@ -32,6 +41,14 @@ from config import (
AppConfig
,
)
import
inspect
import
uuid
import
time
import
json
from
typing
import
Iterator
,
Generator
from
pydantic
import
BaseModel
app
=
FastAPI
()
origins
=
[
"*"
]
...
...
@@ -59,7 +76,7 @@ app.state.config.ENABLE_COMMUNITY_SHARING = ENABLE_COMMUNITY_SHARING
app
.
state
.
MODELS
=
{}
app
.
state
.
TOOLS
=
{}
app
.
state
.
FUNCTIONS
=
{}
app
.
add_middleware
(
CORSMiddleware
,
...
...
@@ -69,17 +86,21 @@ app.add_middleware(
allow_headers
=
[
"*"
],
)
app
.
include_router
(
configs
.
router
,
prefix
=
"/configs"
,
tags
=
[
"configs"
])
app
.
include_router
(
auths
.
router
,
prefix
=
"/auths"
,
tags
=
[
"auths"
])
app
.
include_router
(
users
.
router
,
prefix
=
"/users"
,
tags
=
[
"users"
])
app
.
include_router
(
chats
.
router
,
prefix
=
"/chats"
,
tags
=
[
"chats"
])
app
.
include_router
(
documents
.
router
,
prefix
=
"/documents"
,
tags
=
[
"documents"
])
app
.
include_router
(
tools
.
router
,
prefix
=
"/tools"
,
tags
=
[
"tools"
])
app
.
include_router
(
models
.
router
,
prefix
=
"/models"
,
tags
=
[
"models"
])
app
.
include_router
(
prompts
.
router
,
prefix
=
"/prompts"
,
tags
=
[
"prompts"
])
app
.
include_router
(
memories
.
router
,
prefix
=
"/memories"
,
tags
=
[
"memories"
])
app
.
include_router
(
files
.
router
,
prefix
=
"/files"
,
tags
=
[
"files"
])
app
.
include_router
(
tools
.
router
,
prefix
=
"/tools"
,
tags
=
[
"tools"
])
app
.
include_router
(
functions
.
router
,
prefix
=
"/functions"
,
tags
=
[
"functions"
])
app
.
include_router
(
configs
.
router
,
prefix
=
"/configs"
,
tags
=
[
"configs"
])
app
.
include_router
(
utils
.
router
,
prefix
=
"/utils"
,
tags
=
[
"utils"
])
...
...
@@ -91,3 +112,226 @@ async def get_status():
"default_models"
:
app
.
state
.
config
.
DEFAULT_MODELS
,
"default_prompt_suggestions"
:
app
.
state
.
config
.
DEFAULT_PROMPT_SUGGESTIONS
,
}
async
def
get_pipe_models
():
pipes
=
Functions
.
get_functions_by_type
(
"pipe"
,
active_only
=
True
)
pipe_models
=
[]
for
pipe
in
pipes
:
# Check if function is already loaded
if
pipe
.
id
not
in
app
.
state
.
FUNCTIONS
:
function_module
,
function_type
,
frontmatter
=
load_function_module_by_id
(
pipe
.
id
)
app
.
state
.
FUNCTIONS
[
pipe
.
id
]
=
function_module
else
:
function_module
=
app
.
state
.
FUNCTIONS
[
pipe
.
id
]
if
hasattr
(
function_module
,
"valves"
)
and
hasattr
(
function_module
,
"Valves"
):
print
(
f
"Getting valves for
{
pipe
.
id
}
"
)
valves
=
Functions
.
get_function_valves_by_id
(
pipe
.
id
)
function_module
.
valves
=
function_module
.
Valves
(
**
(
valves
if
valves
else
{})
)
# Check if function is a manifold
if
hasattr
(
function_module
,
"type"
):
if
function_module
.
type
==
"manifold"
:
manifold_pipes
=
[]
# Check if pipes is a function or a list
if
callable
(
function_module
.
pipes
):
manifold_pipes
=
function_module
.
pipes
()
else
:
manifold_pipes
=
function_module
.
pipes
for
p
in
manifold_pipes
:
manifold_pipe_id
=
f
'
{
pipe
.
id
}
.
{
p
[
"id"
]
}
'
manifold_pipe_name
=
p
[
"name"
]
if
hasattr
(
function_module
,
"name"
):
manifold_pipe_name
=
(
f
"
{
function_module
.
name
}{
manifold_pipe_name
}
"
)
pipe_models
.
append
(
{
"id"
:
manifold_pipe_id
,
"name"
:
manifold_pipe_name
,
"object"
:
"model"
,
"created"
:
pipe
.
created_at
,
"owned_by"
:
"openai"
,
"pipe"
:
{
"type"
:
pipe
.
type
},
}
)
else
:
pipe_models
.
append
(
{
"id"
:
pipe
.
id
,
"name"
:
pipe
.
name
,
"object"
:
"model"
,
"created"
:
pipe
.
created_at
,
"owned_by"
:
"openai"
,
"pipe"
:
{
"type"
:
"pipe"
},
}
)
return
pipe_models
async
def
generate_function_chat_completion
(
form_data
,
user
):
async
def
job
():
pipe_id
=
form_data
[
"model"
]
if
"."
in
pipe_id
:
pipe_id
,
sub_pipe_id
=
pipe_id
.
split
(
"."
,
1
)
print
(
pipe_id
)
# Check if function is already loaded
if
pipe_id
not
in
app
.
state
.
FUNCTIONS
:
function_module
,
function_type
,
frontmatter
=
load_function_module_by_id
(
pipe_id
)
app
.
state
.
FUNCTIONS
[
pipe_id
]
=
function_module
else
:
function_module
=
app
.
state
.
FUNCTIONS
[
pipe_id
]
if
hasattr
(
function_module
,
"valves"
)
and
hasattr
(
function_module
,
"Valves"
):
valves
=
Functions
.
get_function_valves_by_id
(
pipe_id
)
function_module
.
valves
=
function_module
.
Valves
(
**
(
valves
if
valves
else
{})
)
pipe
=
function_module
.
pipe
# Get the signature of the function
sig
=
inspect
.
signature
(
pipe
)
params
=
{
"body"
:
form_data
}
if
"__user__"
in
sig
.
parameters
:
__user__
=
{
"id"
:
user
.
id
,
"email"
:
user
.
email
,
"name"
:
user
.
name
,
"role"
:
user
.
role
,
}
try
:
if
hasattr
(
function_module
,
"UserValves"
):
__user__
[
"valves"
]
=
function_module
.
UserValves
(
**
Functions
.
get_user_valves_by_id_and_user_id
(
pipe_id
,
user
.
id
)
)
except
Exception
as
e
:
print
(
e
)
params
=
{
**
params
,
"__user__"
:
__user__
}
if
form_data
[
"stream"
]:
async
def
stream_content
():
try
:
if
inspect
.
iscoroutinefunction
(
pipe
):
res
=
await
pipe
(
**
params
)
else
:
res
=
pipe
(
**
params
)
# Directly return if the response is a StreamingResponse
if
isinstance
(
res
,
StreamingResponse
):
async
for
data
in
res
.
body_iterator
:
yield
data
return
if
isinstance
(
res
,
dict
):
yield
f
"data:
{
json
.
dumps
(
res
)
}
\n\n
"
return
except
Exception
as
e
:
print
(
f
"Error:
{
e
}
"
)
yield
f
"data:
{
json
.
dumps
(
{
'error'
:
{
'detail'
:
str
(
e
)
}}
)
}
\n\n
"
return
if
isinstance
(
res
,
str
):
message
=
stream_message_template
(
form_data
[
"model"
],
res
)
yield
f
"data:
{
json
.
dumps
(
message
)
}
\n\n
"
if
isinstance
(
res
,
Iterator
):
for
line
in
res
:
if
isinstance
(
line
,
BaseModel
):
line
=
line
.
model_dump_json
()
line
=
f
"data:
{
line
}
"
try
:
line
=
line
.
decode
(
"utf-8"
)
except
:
pass
if
line
.
startswith
(
"data:"
):
yield
f
"
{
line
}
\n\n
"
else
:
line
=
stream_message_template
(
form_data
[
"model"
],
line
)
yield
f
"data:
{
json
.
dumps
(
line
)
}
\n\n
"
if
isinstance
(
res
,
str
)
or
isinstance
(
res
,
Generator
):
finish_message
=
{
"id"
:
f
"
{
form_data
[
'model'
]
}
-
{
str
(
uuid
.
uuid4
())
}
"
,
"object"
:
"chat.completion.chunk"
,
"created"
:
int
(
time
.
time
()),
"model"
:
form_data
[
"model"
],
"choices"
:
[
{
"index"
:
0
,
"delta"
:
{},
"logprobs"
:
None
,
"finish_reason"
:
"stop"
,
}
],
}
yield
f
"data:
{
json
.
dumps
(
finish_message
)
}
\n\n
"
yield
f
"data: [DONE]"
return
StreamingResponse
(
stream_content
(),
media_type
=
"text/event-stream"
)
else
:
try
:
if
inspect
.
iscoroutinefunction
(
pipe
):
res
=
await
pipe
(
**
params
)
else
:
res
=
pipe
(
**
params
)
if
isinstance
(
res
,
StreamingResponse
):
return
res
except
Exception
as
e
:
print
(
f
"Error:
{
e
}
"
)
return
{
"error"
:
{
"detail"
:
str
(
e
)}}
if
isinstance
(
res
,
dict
):
return
res
elif
isinstance
(
res
,
BaseModel
):
return
res
.
model_dump
()
else
:
message
=
""
if
isinstance
(
res
,
str
):
message
=
res
if
isinstance
(
res
,
Generator
):
for
stream
in
res
:
message
=
f
"
{
message
}{
stream
}
"
return
{
"id"
:
f
"
{
form_data
[
'model'
]
}
-
{
str
(
uuid
.
uuid4
())
}
"
,
"object"
:
"chat.completion"
,
"created"
:
int
(
time
.
time
()),
"model"
:
form_data
[
"model"
],
"choices"
:
[
{
"index"
:
0
,
"message"
:
{
"role"
:
"assistant"
,
"content"
:
message
,
},
"logprobs"
:
None
,
"finish_reason"
:
"stop"
,
}
],
}
return
await
job
()
backend/apps/webui/models/auths.py
View file @
1eebb85f
...
...
@@ -105,6 +105,7 @@ class AuthsTable:
name
:
str
,
profile_image_url
:
str
=
"/user.png"
,
role
:
str
=
"pending"
,
oauth_sub
:
Optional
[
str
]
=
None
,
)
->
Optional
[
UserModel
]:
log
.
info
(
"insert_new_auth"
)
...
...
@@ -115,7 +116,9 @@ class AuthsTable:
)
result
=
Auth
.
create
(
**
auth
.
model_dump
())
user
=
Users
.
insert_new_user
(
id
,
name
,
email
,
profile_image_url
,
role
)
user
=
Users
.
insert_new_user
(
id
,
name
,
email
,
profile_image_url
,
role
,
oauth_sub
)
if
result
and
user
:
return
user
...
...
backend/apps/webui/models/files.py
0 → 100644
View file @
1eebb85f
from
pydantic
import
BaseModel
from
peewee
import
*
from
playhouse.shortcuts
import
model_to_dict
from
typing
import
List
,
Union
,
Optional
import
time
import
logging
from
apps.webui.internal.db
import
DB
,
JSONField
import
json
from
config
import
SRC_LOG_LEVELS
log
=
logging
.
getLogger
(
__name__
)
log
.
setLevel
(
SRC_LOG_LEVELS
[
"MODELS"
])
####################
# Files DB Schema
####################
class
File
(
Model
):
id
=
CharField
(
unique
=
True
)
user_id
=
CharField
()
filename
=
TextField
()
meta
=
JSONField
()
created_at
=
BigIntegerField
()
class
Meta
:
database
=
DB
class
FileModel
(
BaseModel
):
id
:
str
user_id
:
str
filename
:
str
meta
:
dict
created_at
:
int
# timestamp in epoch
####################
# Forms
####################
class
FileModelResponse
(
BaseModel
):
id
:
str
user_id
:
str
filename
:
str
meta
:
dict
created_at
:
int
# timestamp in epoch
class
FileForm
(
BaseModel
):
id
:
str
filename
:
str
meta
:
dict
=
{}
class
FilesTable
:
def
__init__
(
self
,
db
):
self
.
db
=
db
self
.
db
.
create_tables
([
File
])
def
insert_new_file
(
self
,
user_id
:
str
,
form_data
:
FileForm
)
->
Optional
[
FileModel
]:
file
=
FileModel
(
**
{
**
form_data
.
model_dump
(),
"user_id"
:
user_id
,
"created_at"
:
int
(
time
.
time
()),
}
)
try
:
result
=
File
.
create
(
**
file
.
model_dump
())
if
result
:
return
file
else
:
return
None
except
Exception
as
e
:
print
(
f
"Error creating tool:
{
e
}
"
)
return
None
def
get_file_by_id
(
self
,
id
:
str
)
->
Optional
[
FileModel
]:
try
:
file
=
File
.
get
(
File
.
id
==
id
)
return
FileModel
(
**
model_to_dict
(
file
))
except
:
return
None
def
get_files
(
self
)
->
List
[
FileModel
]:
return
[
FileModel
(
**
model_to_dict
(
file
))
for
file
in
File
.
select
()]
def
delete_file_by_id
(
self
,
id
:
str
)
->
bool
:
try
:
query
=
File
.
delete
().
where
((
File
.
id
==
id
))
query
.
execute
()
# Remove the rows, return number of rows removed.
return
True
except
:
return
False
def
delete_all_files
(
self
)
->
bool
:
try
:
query
=
File
.
delete
()
query
.
execute
()
# Remove the rows, return number of rows removed.
return
True
except
:
return
False
Files
=
FilesTable
(
DB
)
backend/apps/webui/models/functions.py
0 → 100644
View file @
1eebb85f
from
pydantic
import
BaseModel
from
peewee
import
*
from
playhouse.shortcuts
import
model_to_dict
from
typing
import
List
,
Union
,
Optional
import
time
import
logging
from
apps.webui.internal.db
import
DB
,
JSONField
from
apps.webui.models.users
import
Users
import
json
import
copy
from
config
import
SRC_LOG_LEVELS
log
=
logging
.
getLogger
(
__name__
)
log
.
setLevel
(
SRC_LOG_LEVELS
[
"MODELS"
])
####################
# Functions DB Schema
####################
class
Function
(
Model
):
id
=
CharField
(
unique
=
True
)
user_id
=
CharField
()
name
=
TextField
()
type
=
TextField
()
content
=
TextField
()
meta
=
JSONField
()
valves
=
JSONField
()
is_active
=
BooleanField
(
default
=
False
)
is_global
=
BooleanField
(
default
=
False
)
updated_at
=
BigIntegerField
()
created_at
=
BigIntegerField
()
class
Meta
:
database
=
DB
class
FunctionMeta
(
BaseModel
):
description
:
Optional
[
str
]
=
None
manifest
:
Optional
[
dict
]
=
{}
class
FunctionModel
(
BaseModel
):
id
:
str
user_id
:
str
name
:
str
type
:
str
content
:
str
meta
:
FunctionMeta
is_active
:
bool
=
False
is_global
:
bool
=
False
updated_at
:
int
# timestamp in epoch
created_at
:
int
# timestamp in epoch
####################
# Forms
####################
class
FunctionResponse
(
BaseModel
):
id
:
str
user_id
:
str
type
:
str
name
:
str
meta
:
FunctionMeta
is_active
:
bool
is_global
:
bool
updated_at
:
int
# timestamp in epoch
created_at
:
int
# timestamp in epoch
class
FunctionForm
(
BaseModel
):
id
:
str
name
:
str
content
:
str
meta
:
FunctionMeta
class
FunctionValves
(
BaseModel
):
valves
:
Optional
[
dict
]
=
None
class
FunctionsTable
:
def
__init__
(
self
,
db
):
self
.
db
=
db
self
.
db
.
create_tables
([
Function
])
def
insert_new_function
(
self
,
user_id
:
str
,
type
:
str
,
form_data
:
FunctionForm
)
->
Optional
[
FunctionModel
]:
function
=
FunctionModel
(
**
{
**
form_data
.
model_dump
(),
"user_id"
:
user_id
,
"type"
:
type
,
"updated_at"
:
int
(
time
.
time
()),
"created_at"
:
int
(
time
.
time
()),
}
)
try
:
result
=
Function
.
create
(
**
function
.
model_dump
())
if
result
:
return
function
else
:
return
None
except
Exception
as
e
:
print
(
f
"Error creating tool:
{
e
}
"
)
return
None
def
get_function_by_id
(
self
,
id
:
str
)
->
Optional
[
FunctionModel
]:
try
:
function
=
Function
.
get
(
Function
.
id
==
id
)
return
FunctionModel
(
**
model_to_dict
(
function
))
except
:
return
None
def
get_functions
(
self
,
active_only
=
False
)
->
List
[
FunctionModel
]:
if
active_only
:
return
[
FunctionModel
(
**
model_to_dict
(
function
))
for
function
in
Function
.
select
().
where
(
Function
.
is_active
==
True
)
]
else
:
return
[
FunctionModel
(
**
model_to_dict
(
function
))
for
function
in
Function
.
select
()
]
def
get_functions_by_type
(
self
,
type
:
str
,
active_only
=
False
)
->
List
[
FunctionModel
]:
if
active_only
:
return
[
FunctionModel
(
**
model_to_dict
(
function
))
for
function
in
Function
.
select
().
where
(
Function
.
type
==
type
,
Function
.
is_active
==
True
)
]
else
:
return
[
FunctionModel
(
**
model_to_dict
(
function
))
for
function
in
Function
.
select
().
where
(
Function
.
type
==
type
)
]
def
get_global_filter_functions
(
self
)
->
List
[
FunctionModel
]:
return
[
FunctionModel
(
**
model_to_dict
(
function
))
for
function
in
Function
.
select
().
where
(
Function
.
type
==
"filter"
,
Function
.
is_active
==
True
,
Function
.
is_global
==
True
,
)
]
def
get_function_valves_by_id
(
self
,
id
:
str
)
->
Optional
[
dict
]:
try
:
function
=
Function
.
get
(
Function
.
id
==
id
)
return
function
.
valves
if
function
.
valves
else
{}
except
Exception
as
e
:
print
(
f
"An error occurred:
{
e
}
"
)
return
None
def
update_function_valves_by_id
(
self
,
id
:
str
,
valves
:
dict
)
->
Optional
[
FunctionValves
]:
try
:
query
=
Function
.
update
(
**
{
"valves"
:
valves
},
updated_at
=
int
(
time
.
time
()),
).
where
(
Function
.
id
==
id
)
query
.
execute
()
function
=
Function
.
get
(
Function
.
id
==
id
)
return
FunctionValves
(
**
model_to_dict
(
function
))
except
:
return
None
def
get_user_valves_by_id_and_user_id
(
self
,
id
:
str
,
user_id
:
str
)
->
Optional
[
dict
]:
try
:
user
=
Users
.
get_user_by_id
(
user_id
)
user_settings
=
user
.
settings
.
model_dump
()
# Check if user has "functions" and "valves" settings
if
"functions"
not
in
user_settings
:
user_settings
[
"functions"
]
=
{}
if
"valves"
not
in
user_settings
[
"functions"
]:
user_settings
[
"functions"
][
"valves"
]
=
{}
return
user_settings
[
"functions"
][
"valves"
].
get
(
id
,
{})
except
Exception
as
e
:
print
(
f
"An error occurred:
{
e
}
"
)
return
None
def
update_user_valves_by_id_and_user_id
(
self
,
id
:
str
,
user_id
:
str
,
valves
:
dict
)
->
Optional
[
dict
]:
try
:
user
=
Users
.
get_user_by_id
(
user_id
)
user_settings
=
user
.
settings
.
model_dump
()
# Check if user has "functions" and "valves" settings
if
"functions"
not
in
user_settings
:
user_settings
[
"functions"
]
=
{}
if
"valves"
not
in
user_settings
[
"functions"
]:
user_settings
[
"functions"
][
"valves"
]
=
{}
user_settings
[
"functions"
][
"valves"
][
id
]
=
valves
# Update the user settings in the database
query
=
Users
.
update_user_by_id
(
user_id
,
{
"settings"
:
user_settings
})
query
.
execute
()
return
user_settings
[
"functions"
][
"valves"
][
id
]
except
Exception
as
e
:
print
(
f
"An error occurred:
{
e
}
"
)
return
None
def
update_function_by_id
(
self
,
id
:
str
,
updated
:
dict
)
->
Optional
[
FunctionModel
]:
try
:
query
=
Function
.
update
(
**
updated
,
updated_at
=
int
(
time
.
time
()),
).
where
(
Function
.
id
==
id
)
query
.
execute
()
function
=
Function
.
get
(
Function
.
id
==
id
)
return
FunctionModel
(
**
model_to_dict
(
function
))
except
:
return
None
def
deactivate_all_functions
(
self
)
->
Optional
[
bool
]:
try
:
query
=
Function
.
update
(
**
{
"is_active"
:
False
},
updated_at
=
int
(
time
.
time
()),
)
query
.
execute
()
return
True
except
:
return
None
def
delete_function_by_id
(
self
,
id
:
str
)
->
bool
:
try
:
query
=
Function
.
delete
().
where
((
Function
.
id
==
id
))
query
.
execute
()
# Remove the rows, return number of rows removed.
return
True
except
:
return
False
Functions
=
FunctionsTable
(
DB
)
backend/apps/webui/models/tools.py
View file @
1eebb85f
...
...
@@ -5,8 +5,11 @@ from typing import List, Union, Optional
import
time
import
logging
from
apps.webui.internal.db
import
DB
,
JSONField
from
apps.webui.models.users
import
Users
import
json
import
copy
from
config
import
SRC_LOG_LEVELS
...
...
@@ -25,6 +28,7 @@ class Tool(Model):
content
=
TextField
()
specs
=
JSONField
()
meta
=
JSONField
()
valves
=
JSONField
()
updated_at
=
BigIntegerField
()
created_at
=
BigIntegerField
()
...
...
@@ -34,6 +38,7 @@ class Tool(Model):
class
ToolMeta
(
BaseModel
):
description
:
Optional
[
str
]
=
None
manifest
:
Optional
[
dict
]
=
{}
class
ToolModel
(
BaseModel
):
...
...
@@ -68,6 +73,10 @@ class ToolForm(BaseModel):
meta
:
ToolMeta
class
ToolValves
(
BaseModel
):
valves
:
Optional
[
dict
]
=
None
class
ToolsTable
:
def
__init__
(
self
,
db
):
self
.
db
=
db
...
...
@@ -106,6 +115,69 @@ class ToolsTable:
def
get_tools
(
self
)
->
List
[
ToolModel
]:
return
[
ToolModel
(
**
model_to_dict
(
tool
))
for
tool
in
Tool
.
select
()]
def
get_tool_valves_by_id
(
self
,
id
:
str
)
->
Optional
[
dict
]:
try
:
tool
=
Tool
.
get
(
Tool
.
id
==
id
)
return
tool
.
valves
if
tool
.
valves
else
{}
except
Exception
as
e
:
print
(
f
"An error occurred:
{
e
}
"
)
return
None
def
update_tool_valves_by_id
(
self
,
id
:
str
,
valves
:
dict
)
->
Optional
[
ToolValves
]:
try
:
query
=
Tool
.
update
(
**
{
"valves"
:
valves
},
updated_at
=
int
(
time
.
time
()),
).
where
(
Tool
.
id
==
id
)
query
.
execute
()
tool
=
Tool
.
get
(
Tool
.
id
==
id
)
return
ToolValves
(
**
model_to_dict
(
tool
))
except
:
return
None
def
get_user_valves_by_id_and_user_id
(
self
,
id
:
str
,
user_id
:
str
)
->
Optional
[
dict
]:
try
:
user
=
Users
.
get_user_by_id
(
user_id
)
user_settings
=
user
.
settings
.
model_dump
()
# Check if user has "tools" and "valves" settings
if
"tools"
not
in
user_settings
:
user_settings
[
"tools"
]
=
{}
if
"valves"
not
in
user_settings
[
"tools"
]:
user_settings
[
"tools"
][
"valves"
]
=
{}
return
user_settings
[
"tools"
][
"valves"
].
get
(
id
,
{})
except
Exception
as
e
:
print
(
f
"An error occurred:
{
e
}
"
)
return
None
def
update_user_valves_by_id_and_user_id
(
self
,
id
:
str
,
user_id
:
str
,
valves
:
dict
)
->
Optional
[
dict
]:
try
:
user
=
Users
.
get_user_by_id
(
user_id
)
user_settings
=
user
.
settings
.
model_dump
()
# Check if user has "tools" and "valves" settings
if
"tools"
not
in
user_settings
:
user_settings
[
"tools"
]
=
{}
if
"valves"
not
in
user_settings
[
"tools"
]:
user_settings
[
"tools"
][
"valves"
]
=
{}
user_settings
[
"tools"
][
"valves"
][
id
]
=
valves
# Update the user settings in the database
query
=
Users
.
update_user_by_id
(
user_id
,
{
"settings"
:
user_settings
})
query
.
execute
()
return
user_settings
[
"tools"
][
"valves"
][
id
]
except
Exception
as
e
:
print
(
f
"An error occurred:
{
e
}
"
)
return
None
def
update_tool_by_id
(
self
,
id
:
str
,
updated
:
dict
)
->
Optional
[
ToolModel
]:
try
:
query
=
Tool
.
update
(
...
...
backend/apps/webui/models/users.py
View file @
1eebb85f
...
...
@@ -28,6 +28,8 @@ class User(Model):
settings
=
JSONField
(
null
=
True
)
info
=
JSONField
(
null
=
True
)
oauth_sub
=
TextField
(
null
=
True
,
unique
=
True
)
class
Meta
:
database
=
DB
...
...
@@ -53,6 +55,8 @@ class UserModel(BaseModel):
settings
:
Optional
[
UserSettings
]
=
None
info
:
Optional
[
dict
]
=
None
oauth_sub
:
Optional
[
str
]
=
None
####################
# Forms
...
...
@@ -83,6 +87,7 @@ class UsersTable:
email
:
str
,
profile_image_url
:
str
=
"/user.png"
,
role
:
str
=
"pending"
,
oauth_sub
:
Optional
[
str
]
=
None
,
)
->
Optional
[
UserModel
]:
user
=
UserModel
(
**
{
...
...
@@ -94,6 +99,7 @@ class UsersTable:
"last_active_at"
:
int
(
time
.
time
()),
"created_at"
:
int
(
time
.
time
()),
"updated_at"
:
int
(
time
.
time
()),
"oauth_sub"
:
oauth_sub
,
}
)
result
=
User
.
create
(
**
user
.
model_dump
())
...
...
@@ -123,6 +129,13 @@ class UsersTable:
except
:
return
None
def
get_user_by_oauth_sub
(
self
,
sub
:
str
)
->
Optional
[
UserModel
]:
try
:
user
=
User
.
get
(
User
.
oauth_sub
==
sub
)
return
UserModel
(
**
model_to_dict
(
user
))
except
:
return
None
def
get_users
(
self
,
skip
:
int
=
0
,
limit
:
int
=
50
)
->
List
[
UserModel
]:
return
[
UserModel
(
**
model_to_dict
(
user
))
...
...
@@ -174,6 +187,18 @@ class UsersTable:
except
:
return
None
def
update_user_oauth_sub_by_id
(
self
,
id
:
str
,
oauth_sub
:
str
)
->
Optional
[
UserModel
]:
try
:
query
=
User
.
update
(
oauth_sub
=
oauth_sub
).
where
(
User
.
id
==
id
)
query
.
execute
()
user
=
User
.
get
(
User
.
id
==
id
)
return
UserModel
(
**
model_to_dict
(
user
))
except
:
return
None
def
update_user_by_id
(
self
,
id
:
str
,
updated
:
dict
)
->
Optional
[
UserModel
]:
try
:
query
=
User
.
update
(
**
updated
).
where
(
User
.
id
==
id
)
...
...
backend/apps/webui/routers/auths.py
View file @
1eebb85f
...
...
@@ -2,6 +2,7 @@ import logging
from
fastapi
import
Request
,
UploadFile
,
File
from
fastapi
import
Depends
,
HTTPException
,
status
from
fastapi.responses
import
Response
from
fastapi
import
APIRouter
from
pydantic
import
BaseModel
...
...
@@ -9,7 +10,6 @@ import re
import
uuid
import
csv
from
apps.webui.models.auths
import
(
SigninForm
,
SignupForm
,
...
...
@@ -47,7 +47,21 @@ router = APIRouter()
@
router
.
get
(
"/"
,
response_model
=
UserResponse
)
async
def
get_session_user
(
user
=
Depends
(
get_current_user
)):
async
def
get_session_user
(
request
:
Request
,
response
:
Response
,
user
=
Depends
(
get_current_user
)
):
token
=
create_token
(
data
=
{
"id"
:
user
.
id
},
expires_delta
=
parse_duration
(
request
.
app
.
state
.
config
.
JWT_EXPIRES_IN
),
)
# Set the cookie token
response
.
set_cookie
(
key
=
"token"
,
value
=
token
,
httponly
=
True
,
# Ensures the cookie is not accessible via JavaScript
)
return
{
"id"
:
user
.
id
,
"email"
:
user
.
email
,
...
...
@@ -108,7 +122,7 @@ async def update_password(
@
router
.
post
(
"/signin"
,
response_model
=
SigninResponse
)
async
def
signin
(
request
:
Request
,
form_data
:
SigninForm
):
async
def
signin
(
request
:
Request
,
response
:
Response
,
form_data
:
SigninForm
):
if
WEBUI_AUTH_TRUSTED_EMAIL_HEADER
:
if
WEBUI_AUTH_TRUSTED_EMAIL_HEADER
not
in
request
.
headers
:
raise
HTTPException
(
400
,
detail
=
ERROR_MESSAGES
.
INVALID_TRUSTED_HEADER
)
...
...
@@ -152,6 +166,13 @@ async def signin(request: Request, form_data: SigninForm):
expires_delta
=
parse_duration
(
request
.
app
.
state
.
config
.
JWT_EXPIRES_IN
),
)
# Set the cookie token
response
.
set_cookie
(
key
=
"token"
,
value
=
token
,
httponly
=
True
,
# Ensures the cookie is not accessible via JavaScript
)
return
{
"token"
:
token
,
"token_type"
:
"Bearer"
,
...
...
@@ -171,7 +192,7 @@ async def signin(request: Request, form_data: SigninForm):
@
router
.
post
(
"/signup"
,
response_model
=
SigninResponse
)
async
def
signup
(
request
:
Request
,
form_data
:
SignupForm
):
async
def
signup
(
request
:
Request
,
response
:
Response
,
form_data
:
SignupForm
):
if
not
request
.
app
.
state
.
config
.
ENABLE_SIGNUP
and
WEBUI_AUTH
:
raise
HTTPException
(
status
.
HTTP_403_FORBIDDEN
,
detail
=
ERROR_MESSAGES
.
ACCESS_PROHIBITED
...
...
@@ -207,6 +228,13 @@ async def signup(request: Request, form_data: SignupForm):
)
# response.set_cookie(key='token', value=token, httponly=True)
# Set the cookie token
response
.
set_cookie
(
key
=
"token"
,
value
=
token
,
httponly
=
True
,
# Ensures the cookie is not accessible via JavaScript
)
if
request
.
app
.
state
.
config
.
WEBHOOK_URL
:
post_webhook
(
request
.
app
.
state
.
config
.
WEBHOOK_URL
,
...
...
backend/apps/webui/routers/chats.py
View file @
1eebb85f
from
fastapi
import
Depends
,
Request
,
HTTPException
,
status
from
datetime
import
datetime
,
timedelta
from
typing
import
List
,
Union
,
Optional
from
utils.utils
import
get_
current
_user
,
get_admin_user
from
utils.utils
import
get_
verified
_user
,
get_admin_user
from
fastapi
import
APIRouter
from
pydantic
import
BaseModel
import
json
...
...
@@ -43,7 +43,7 @@ router = APIRouter()
@
router
.
get
(
"/"
,
response_model
=
List
[
ChatTitleIdResponse
])
@
router
.
get
(
"/list"
,
response_model
=
List
[
ChatTitleIdResponse
])
async
def
get_session_user_chat_list
(
user
=
Depends
(
get_
current
_user
),
skip
:
int
=
0
,
limit
:
int
=
50
user
=
Depends
(
get_
verified
_user
),
skip
:
int
=
0
,
limit
:
int
=
50
):
return
Chats
.
get_chat_list_by_user_id
(
user
.
id
,
skip
,
limit
)
...
...
@@ -54,7 +54,7 @@ async def get_session_user_chat_list(
@
router
.
delete
(
"/"
,
response_model
=
bool
)
async
def
delete_all_user_chats
(
request
:
Request
,
user
=
Depends
(
get_
current
_user
)):
async
def
delete_all_user_chats
(
request
:
Request
,
user
=
Depends
(
get_
verified
_user
)):
if
(
user
.
role
==
"user"
...
...
@@ -89,7 +89,7 @@ async def get_user_chat_list_by_user_id(
@
router
.
post
(
"/new"
,
response_model
=
Optional
[
ChatResponse
])
async
def
create_new_chat
(
form_data
:
ChatForm
,
user
=
Depends
(
get_
current
_user
)):
async
def
create_new_chat
(
form_data
:
ChatForm
,
user
=
Depends
(
get_
verified
_user
)):
try
:
chat
=
Chats
.
insert_new_chat
(
user
.
id
,
form_data
)
return
ChatResponse
(
**
{
**
chat
.
model_dump
(),
"chat"
:
json
.
loads
(
chat
.
chat
)})
...
...
@@ -106,7 +106,7 @@ async def create_new_chat(form_data: ChatForm, user=Depends(get_current_user)):
@
router
.
get
(
"/all"
,
response_model
=
List
[
ChatResponse
])
async
def
get_user_chats
(
user
=
Depends
(
get_
current
_user
)):
async
def
get_user_chats
(
user
=
Depends
(
get_
verified
_user
)):
return
[
ChatResponse
(
**
{
**
chat
.
model_dump
(),
"chat"
:
json
.
loads
(
chat
.
chat
)})
for
chat
in
Chats
.
get_chats_by_user_id
(
user
.
id
)
...
...
@@ -119,7 +119,7 @@ async def get_user_chats(user=Depends(get_current_user)):
@
router
.
get
(
"/all/archived"
,
response_model
=
List
[
ChatResponse
])
async
def
get_user_chats
(
user
=
Depends
(
get_
current
_user
)):
async
def
get_user_chats
(
user
=
Depends
(
get_
verified
_user
)):
return
[
ChatResponse
(
**
{
**
chat
.
model_dump
(),
"chat"
:
json
.
loads
(
chat
.
chat
)})
for
chat
in
Chats
.
get_archived_chats_by_user_id
(
user
.
id
)
...
...
@@ -151,7 +151,7 @@ async def get_all_user_chats_in_db(user=Depends(get_admin_user)):
@
router
.
get
(
"/archived"
,
response_model
=
List
[
ChatTitleIdResponse
])
async
def
get_archived_session_user_chat_list
(
user
=
Depends
(
get_
current
_user
),
skip
:
int
=
0
,
limit
:
int
=
50
user
=
Depends
(
get_
verified
_user
),
skip
:
int
=
0
,
limit
:
int
=
50
):
return
Chats
.
get_archived_chat_list_by_user_id
(
user
.
id
,
skip
,
limit
)
...
...
@@ -162,7 +162,7 @@ async def get_archived_session_user_chat_list(
@
router
.
post
(
"/archive/all"
,
response_model
=
bool
)
async
def
archive_all_chats
(
user
=
Depends
(
get_
current
_user
)):
async
def
archive_all_chats
(
user
=
Depends
(
get_
verified
_user
)):
return
Chats
.
archive_all_chats_by_user_id
(
user
.
id
)
...
...
@@ -172,7 +172,7 @@ async def archive_all_chats(user=Depends(get_current_user)):
@
router
.
get
(
"/share/{share_id}"
,
response_model
=
Optional
[
ChatResponse
])
async
def
get_shared_chat_by_id
(
share_id
:
str
,
user
=
Depends
(
get_
current
_user
)):
async
def
get_shared_chat_by_id
(
share_id
:
str
,
user
=
Depends
(
get_
verified
_user
)):
if
user
.
role
==
"pending"
:
raise
HTTPException
(
status_code
=
status
.
HTTP_401_UNAUTHORIZED
,
detail
=
ERROR_MESSAGES
.
NOT_FOUND
...
...
@@ -204,7 +204,7 @@ class TagNameForm(BaseModel):
@
router
.
post
(
"/tags"
,
response_model
=
List
[
ChatTitleIdResponse
])
async
def
get_user_chat_list_by_tag_name
(
form_data
:
TagNameForm
,
user
=
Depends
(
get_
current
_user
)
form_data
:
TagNameForm
,
user
=
Depends
(
get_
verified
_user
)
):
print
(
form_data
)
...
...
@@ -229,7 +229,7 @@ async def get_user_chat_list_by_tag_name(
@
router
.
get
(
"/tags/all"
,
response_model
=
List
[
TagModel
])
async
def
get_all_tags
(
user
=
Depends
(
get_
current
_user
)):
async
def
get_all_tags
(
user
=
Depends
(
get_
verified
_user
)):
try
:
tags
=
Tags
.
get_tags_by_user_id
(
user
.
id
)
return
tags
...
...
@@ -246,7 +246,7 @@ async def get_all_tags(user=Depends(get_current_user)):
@
router
.
get
(
"/{id}"
,
response_model
=
Optional
[
ChatResponse
])
async
def
get_chat_by_id
(
id
:
str
,
user
=
Depends
(
get_
current
_user
)):
async
def
get_chat_by_id
(
id
:
str
,
user
=
Depends
(
get_
verified
_user
)):
chat
=
Chats
.
get_chat_by_id_and_user_id
(
id
,
user
.
id
)
if
chat
:
...
...
@@ -264,7 +264,7 @@ async def get_chat_by_id(id: str, user=Depends(get_current_user)):
@
router
.
post
(
"/{id}"
,
response_model
=
Optional
[
ChatResponse
])
async
def
update_chat_by_id
(
id
:
str
,
form_data
:
ChatForm
,
user
=
Depends
(
get_
current
_user
)
id
:
str
,
form_data
:
ChatForm
,
user
=
Depends
(
get_
verified
_user
)
):
chat
=
Chats
.
get_chat_by_id_and_user_id
(
id
,
user
.
id
)
if
chat
:
...
...
@@ -285,7 +285,7 @@ async def update_chat_by_id(
@
router
.
delete
(
"/{id}"
,
response_model
=
bool
)
async
def
delete_chat_by_id
(
request
:
Request
,
id
:
str
,
user
=
Depends
(
get_
current
_user
)):
async
def
delete_chat_by_id
(
request
:
Request
,
id
:
str
,
user
=
Depends
(
get_
verified
_user
)):
if
user
.
role
==
"admin"
:
result
=
Chats
.
delete_chat_by_id
(
id
)
...
...
@@ -307,7 +307,7 @@ async def delete_chat_by_id(request: Request, id: str, user=Depends(get_current_
@
router
.
get
(
"/{id}/clone"
,
response_model
=
Optional
[
ChatResponse
])
async
def
clone_chat_by_id
(
id
:
str
,
user
=
Depends
(
get_
current
_user
)):
async
def
clone_chat_by_id
(
id
:
str
,
user
=
Depends
(
get_
verified
_user
)):
chat
=
Chats
.
get_chat_by_id_and_user_id
(
id
,
user
.
id
)
if
chat
:
...
...
@@ -333,7 +333,7 @@ async def clone_chat_by_id(id: str, user=Depends(get_current_user)):
@
router
.
get
(
"/{id}/archive"
,
response_model
=
Optional
[
ChatResponse
])
async
def
archive_chat_by_id
(
id
:
str
,
user
=
Depends
(
get_
current
_user
)):
async
def
archive_chat_by_id
(
id
:
str
,
user
=
Depends
(
get_
verified
_user
)):
chat
=
Chats
.
get_chat_by_id_and_user_id
(
id
,
user
.
id
)
if
chat
:
chat
=
Chats
.
toggle_chat_archive_by_id
(
id
)
...
...
@@ -350,7 +350,7 @@ async def archive_chat_by_id(id: str, user=Depends(get_current_user)):
@
router
.
post
(
"/{id}/share"
,
response_model
=
Optional
[
ChatResponse
])
async
def
share_chat_by_id
(
id
:
str
,
user
=
Depends
(
get_
current
_user
)):
async
def
share_chat_by_id
(
id
:
str
,
user
=
Depends
(
get_
verified
_user
)):
chat
=
Chats
.
get_chat_by_id_and_user_id
(
id
,
user
.
id
)
if
chat
:
if
chat
.
share_id
:
...
...
@@ -382,7 +382,7 @@ async def share_chat_by_id(id: str, user=Depends(get_current_user)):
@
router
.
delete
(
"/{id}/share"
,
response_model
=
Optional
[
bool
])
async
def
delete_shared_chat_by_id
(
id
:
str
,
user
=
Depends
(
get_
current
_user
)):
async
def
delete_shared_chat_by_id
(
id
:
str
,
user
=
Depends
(
get_
verified
_user
)):
chat
=
Chats
.
get_chat_by_id_and_user_id
(
id
,
user
.
id
)
if
chat
:
if
not
chat
.
share_id
:
...
...
@@ -405,7 +405,7 @@ async def delete_shared_chat_by_id(id: str, user=Depends(get_current_user)):
@
router
.
get
(
"/{id}/tags"
,
response_model
=
List
[
TagModel
])
async
def
get_chat_tags_by_id
(
id
:
str
,
user
=
Depends
(
get_
current
_user
)):
async
def
get_chat_tags_by_id
(
id
:
str
,
user
=
Depends
(
get_
verified
_user
)):
tags
=
Tags
.
get_tags_by_chat_id_and_user_id
(
id
,
user
.
id
)
if
tags
!=
None
:
...
...
@@ -423,7 +423,7 @@ async def get_chat_tags_by_id(id: str, user=Depends(get_current_user)):
@
router
.
post
(
"/{id}/tags"
,
response_model
=
Optional
[
ChatIdTagModel
])
async
def
add_chat_tag_by_id
(
id
:
str
,
form_data
:
ChatIdTagForm
,
user
=
Depends
(
get_
current
_user
)
id
:
str
,
form_data
:
ChatIdTagForm
,
user
=
Depends
(
get_
verified
_user
)
):
tags
=
Tags
.
get_tags_by_chat_id_and_user_id
(
id
,
user
.
id
)
...
...
@@ -450,7 +450,7 @@ async def add_chat_tag_by_id(
@
router
.
delete
(
"/{id}/tags"
,
response_model
=
Optional
[
bool
])
async
def
delete_chat_tag_by_id
(
id
:
str
,
form_data
:
ChatIdTagForm
,
user
=
Depends
(
get_
current
_user
)
id
:
str
,
form_data
:
ChatIdTagForm
,
user
=
Depends
(
get_
verified
_user
)
):
result
=
Tags
.
delete_tag_by_tag_name_and_chat_id_and_user_id
(
form_data
.
tag_name
,
id
,
user
.
id
...
...
@@ -470,7 +470,7 @@ async def delete_chat_tag_by_id(
@
router
.
delete
(
"/{id}/tags/all"
,
response_model
=
Optional
[
bool
])
async
def
delete_all_chat_tags_by_id
(
id
:
str
,
user
=
Depends
(
get_
current
_user
)):
async
def
delete_all_chat_tags_by_id
(
id
:
str
,
user
=
Depends
(
get_
verified
_user
)):
result
=
Tags
.
delete_tags_by_chat_id_and_user_id
(
id
,
user
.
id
)
if
result
:
...
...
backend/apps/webui/routers/configs.py
View file @
1eebb85f
...
...
@@ -14,7 +14,7 @@ from apps.webui.models.users import Users
from
utils.utils
import
(
get_password_hash
,
get_
current
_user
,
get_
verified
_user
,
get_admin_user
,
create_token
,
)
...
...
@@ -84,6 +84,6 @@ async def set_banners(
@
router
.
get
(
"/banners"
,
response_model
=
List
[
BannerModel
])
async
def
get_banners
(
request
:
Request
,
user
=
Depends
(
get_
current
_user
),
user
=
Depends
(
get_
verified
_user
),
):
return
request
.
app
.
state
.
config
.
BANNERS
backend/apps/webui/routers/documents.py
View file @
1eebb85f
...
...
@@ -14,7 +14,7 @@ from apps.webui.models.documents import (
DocumentResponse
,
)
from
utils.utils
import
get_
current
_user
,
get_admin_user
from
utils.utils
import
get_
verified
_user
,
get_admin_user
from
constants
import
ERROR_MESSAGES
router
=
APIRouter
()
...
...
@@ -25,7 +25,7 @@ router = APIRouter()
@
router
.
get
(
"/"
,
response_model
=
List
[
DocumentResponse
])
async
def
get_documents
(
user
=
Depends
(
get_
current
_user
)):
async
def
get_documents
(
user
=
Depends
(
get_
verified
_user
)):
docs
=
[
DocumentResponse
(
**
{
...
...
@@ -74,7 +74,7 @@ async def create_new_doc(form_data: DocumentForm, user=Depends(get_admin_user)):
@
router
.
get
(
"/doc"
,
response_model
=
Optional
[
DocumentResponse
])
async
def
get_doc_by_name
(
name
:
str
,
user
=
Depends
(
get_
current
_user
)):
async
def
get_doc_by_name
(
name
:
str
,
user
=
Depends
(
get_
verified
_user
)):
doc
=
Documents
.
get_doc_by_name
(
name
)
if
doc
:
...
...
@@ -106,7 +106,7 @@ class TagDocumentForm(BaseModel):
@
router
.
post
(
"/doc/tags"
,
response_model
=
Optional
[
DocumentResponse
])
async
def
tag_doc_by_name
(
form_data
:
TagDocumentForm
,
user
=
Depends
(
get_
current
_user
)):
async
def
tag_doc_by_name
(
form_data
:
TagDocumentForm
,
user
=
Depends
(
get_
verified
_user
)):
doc
=
Documents
.
update_doc_content_by_name
(
form_data
.
name
,
{
"tags"
:
form_data
.
tags
})
if
doc
:
...
...
backend/apps/webui/routers/files.py
0 → 100644
View file @
1eebb85f
from
fastapi
import
(
Depends
,
FastAPI
,
HTTPException
,
status
,
Request
,
UploadFile
,
File
,
Form
,
)
from
datetime
import
datetime
,
timedelta
from
typing
import
List
,
Union
,
Optional
from
pathlib
import
Path
from
fastapi
import
APIRouter
from
fastapi.responses
import
StreamingResponse
,
JSONResponse
,
FileResponse
from
pydantic
import
BaseModel
import
json
from
apps.webui.models.files
import
(
Files
,
FileForm
,
FileModel
,
FileModelResponse
,
)
from
utils.utils
import
get_verified_user
,
get_admin_user
from
constants
import
ERROR_MESSAGES
from
importlib
import
util
import
os
import
uuid
import
os
,
shutil
,
logging
,
re
from
config
import
SRC_LOG_LEVELS
,
UPLOAD_DIR
log
=
logging
.
getLogger
(
__name__
)
log
.
setLevel
(
SRC_LOG_LEVELS
[
"MODELS"
])
router
=
APIRouter
()
############################
# Upload File
############################
@
router
.
post
(
"/"
)
def
upload_file
(
file
:
UploadFile
=
File
(...),
user
=
Depends
(
get_verified_user
),
):
log
.
info
(
f
"file.content_type:
{
file
.
content_type
}
"
)
try
:
unsanitized_filename
=
file
.
filename
filename
=
os
.
path
.
basename
(
unsanitized_filename
)
# replace filename with uuid
id
=
str
(
uuid
.
uuid4
())
filename
=
f
"
{
id
}
_
{
filename
}
"
file_path
=
f
"
{
UPLOAD_DIR
}
/
{
filename
}
"
contents
=
file
.
file
.
read
()
with
open
(
file_path
,
"wb"
)
as
f
:
f
.
write
(
contents
)
f
.
close
()
file
=
Files
.
insert_new_file
(
user
.
id
,
FileForm
(
**
{
"id"
:
id
,
"filename"
:
filename
,
"meta"
:
{
"content_type"
:
file
.
content_type
,
"size"
:
len
(
contents
),
"path"
:
file_path
,
},
}
),
)
if
file
:
return
file
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_400_BAD_REQUEST
,
detail
=
ERROR_MESSAGES
.
DEFAULT
(
"Error uploading file"
),
)
except
Exception
as
e
:
log
.
exception
(
e
)
raise
HTTPException
(
status_code
=
status
.
HTTP_400_BAD_REQUEST
,
detail
=
ERROR_MESSAGES
.
DEFAULT
(
e
),
)
############################
# List Files
############################
@
router
.
get
(
"/"
,
response_model
=
List
[
FileModel
])
async
def
list_files
(
user
=
Depends
(
get_verified_user
)):
files
=
Files
.
get_files
()
return
files
############################
# Delete All Files
############################
@
router
.
delete
(
"/all"
)
async
def
delete_all_files
(
user
=
Depends
(
get_admin_user
)):
result
=
Files
.
delete_all_files
()
if
result
:
folder
=
f
"
{
UPLOAD_DIR
}
"
try
:
# Check if the directory exists
if
os
.
path
.
exists
(
folder
):
# Iterate over all the files and directories in the specified directory
for
filename
in
os
.
listdir
(
folder
):
file_path
=
os
.
path
.
join
(
folder
,
filename
)
try
:
if
os
.
path
.
isfile
(
file_path
)
or
os
.
path
.
islink
(
file_path
):
os
.
unlink
(
file_path
)
# Remove the file or link
elif
os
.
path
.
isdir
(
file_path
):
shutil
.
rmtree
(
file_path
)
# Remove the directory
except
Exception
as
e
:
print
(
f
"Failed to delete
{
file_path
}
. Reason:
{
e
}
"
)
else
:
print
(
f
"The directory
{
folder
}
does not exist"
)
except
Exception
as
e
:
print
(
f
"Failed to process the directory
{
folder
}
. Reason:
{
e
}
"
)
return
{
"message"
:
"All files deleted successfully"
}
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_400_BAD_REQUEST
,
detail
=
ERROR_MESSAGES
.
DEFAULT
(
"Error deleting files"
),
)
############################
# Get File By Id
############################
@
router
.
get
(
"/{id}"
,
response_model
=
Optional
[
FileModel
])
async
def
get_file_by_id
(
id
:
str
,
user
=
Depends
(
get_verified_user
)):
file
=
Files
.
get_file_by_id
(
id
)
if
file
:
return
file
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_404_NOT_FOUND
,
detail
=
ERROR_MESSAGES
.
NOT_FOUND
,
)
############################
# Get File Content By Id
############################
@
router
.
get
(
"/{id}/content"
,
response_model
=
Optional
[
FileModel
])
async
def
get_file_content_by_id
(
id
:
str
,
user
=
Depends
(
get_verified_user
)):
file
=
Files
.
get_file_by_id
(
id
)
if
file
:
file_path
=
Path
(
file
.
meta
[
"path"
])
# Check if the file already exists in the cache
if
file_path
.
is_file
():
print
(
f
"file_path:
{
file_path
}
"
)
return
FileResponse
(
file_path
)
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_404_NOT_FOUND
,
detail
=
ERROR_MESSAGES
.
NOT_FOUND
,
)
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_404_NOT_FOUND
,
detail
=
ERROR_MESSAGES
.
NOT_FOUND
,
)
@
router
.
get
(
"/{id}/content/{file_name}"
,
response_model
=
Optional
[
FileModel
])
async
def
get_file_content_by_id
(
id
:
str
,
user
=
Depends
(
get_verified_user
)):
file
=
Files
.
get_file_by_id
(
id
)
if
file
:
file_path
=
Path
(
file
.
meta
[
"path"
])
# Check if the file already exists in the cache
if
file_path
.
is_file
():
print
(
f
"file_path:
{
file_path
}
"
)
return
FileResponse
(
file_path
)
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_404_NOT_FOUND
,
detail
=
ERROR_MESSAGES
.
NOT_FOUND
,
)
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_404_NOT_FOUND
,
detail
=
ERROR_MESSAGES
.
NOT_FOUND
,
)
############################
# Delete File By Id
############################
@
router
.
delete
(
"/{id}"
)
async
def
delete_file_by_id
(
id
:
str
,
user
=
Depends
(
get_verified_user
)):
file
=
Files
.
get_file_by_id
(
id
)
if
file
:
result
=
Files
.
delete_file_by_id
(
id
)
if
result
:
return
{
"message"
:
"File deleted successfully"
}
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_400_BAD_REQUEST
,
detail
=
ERROR_MESSAGES
.
DEFAULT
(
"Error deleting file"
),
)
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_404_NOT_FOUND
,
detail
=
ERROR_MESSAGES
.
NOT_FOUND
,
)
backend/apps/webui/routers/functions.py
0 → 100644
View file @
1eebb85f
from
fastapi
import
Depends
,
FastAPI
,
HTTPException
,
status
,
Request
from
datetime
import
datetime
,
timedelta
from
typing
import
List
,
Union
,
Optional
from
fastapi
import
APIRouter
from
pydantic
import
BaseModel
import
json
from
apps.webui.models.functions
import
(
Functions
,
FunctionForm
,
FunctionModel
,
FunctionResponse
,
)
from
apps.webui.utils
import
load_function_module_by_id
from
utils.utils
import
get_verified_user
,
get_admin_user
from
constants
import
ERROR_MESSAGES
from
importlib
import
util
import
os
from
pathlib
import
Path
from
config
import
DATA_DIR
,
CACHE_DIR
,
FUNCTIONS_DIR
router
=
APIRouter
()
############################
# GetFunctions
############################
@
router
.
get
(
"/"
,
response_model
=
List
[
FunctionResponse
])
async
def
get_functions
(
user
=
Depends
(
get_verified_user
)):
return
Functions
.
get_functions
()
############################
# ExportFunctions
############################
@
router
.
get
(
"/export"
,
response_model
=
List
[
FunctionModel
])
async
def
get_functions
(
user
=
Depends
(
get_admin_user
)):
return
Functions
.
get_functions
()
############################
# CreateNewFunction
############################
@
router
.
post
(
"/create"
,
response_model
=
Optional
[
FunctionResponse
])
async
def
create_new_function
(
request
:
Request
,
form_data
:
FunctionForm
,
user
=
Depends
(
get_admin_user
)
):
if
not
form_data
.
id
.
isidentifier
():
raise
HTTPException
(
status_code
=
status
.
HTTP_400_BAD_REQUEST
,
detail
=
"Only alphanumeric characters and underscores are allowed in the id"
,
)
form_data
.
id
=
form_data
.
id
.
lower
()
function
=
Functions
.
get_function_by_id
(
form_data
.
id
)
if
function
==
None
:
function_path
=
os
.
path
.
join
(
FUNCTIONS_DIR
,
f
"
{
form_data
.
id
}
.py"
)
try
:
with
open
(
function_path
,
"w"
)
as
function_file
:
function_file
.
write
(
form_data
.
content
)
function_module
,
function_type
,
frontmatter
=
load_function_module_by_id
(
form_data
.
id
)
form_data
.
meta
.
manifest
=
frontmatter
FUNCTIONS
=
request
.
app
.
state
.
FUNCTIONS
FUNCTIONS
[
form_data
.
id
]
=
function_module
function
=
Functions
.
insert_new_function
(
user
.
id
,
function_type
,
form_data
)
function_cache_dir
=
Path
(
CACHE_DIR
)
/
"functions"
/
form_data
.
id
function_cache_dir
.
mkdir
(
parents
=
True
,
exist_ok
=
True
)
if
function
:
return
function
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_400_BAD_REQUEST
,
detail
=
ERROR_MESSAGES
.
DEFAULT
(
"Error creating function"
),
)
except
Exception
as
e
:
print
(
e
)
raise
HTTPException
(
status_code
=
status
.
HTTP_400_BAD_REQUEST
,
detail
=
ERROR_MESSAGES
.
DEFAULT
(
e
),
)
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_400_BAD_REQUEST
,
detail
=
ERROR_MESSAGES
.
ID_TAKEN
,
)
############################
# GetFunctionById
############################
@
router
.
get
(
"/id/{id}"
,
response_model
=
Optional
[
FunctionModel
])
async
def
get_function_by_id
(
id
:
str
,
user
=
Depends
(
get_admin_user
)):
function
=
Functions
.
get_function_by_id
(
id
)
if
function
:
return
function
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_401_UNAUTHORIZED
,
detail
=
ERROR_MESSAGES
.
NOT_FOUND
,
)
############################
# ToggleFunctionById
############################
@
router
.
post
(
"/id/{id}/toggle"
,
response_model
=
Optional
[
FunctionModel
])
async
def
toggle_function_by_id
(
id
:
str
,
user
=
Depends
(
get_admin_user
)):
function
=
Functions
.
get_function_by_id
(
id
)
if
function
:
function
=
Functions
.
update_function_by_id
(
id
,
{
"is_active"
:
not
function
.
is_active
}
)
if
function
:
return
function
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_400_BAD_REQUEST
,
detail
=
ERROR_MESSAGES
.
DEFAULT
(
"Error updating function"
),
)
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_401_UNAUTHORIZED
,
detail
=
ERROR_MESSAGES
.
NOT_FOUND
,
)
############################
# ToggleGlobalById
############################
@
router
.
post
(
"/id/{id}/toggle/global"
,
response_model
=
Optional
[
FunctionModel
])
async
def
toggle_global_by_id
(
id
:
str
,
user
=
Depends
(
get_admin_user
)):
function
=
Functions
.
get_function_by_id
(
id
)
if
function
:
function
=
Functions
.
update_function_by_id
(
id
,
{
"is_global"
:
not
function
.
is_global
}
)
if
function
:
return
function
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_400_BAD_REQUEST
,
detail
=
ERROR_MESSAGES
.
DEFAULT
(
"Error updating function"
),
)
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_401_UNAUTHORIZED
,
detail
=
ERROR_MESSAGES
.
NOT_FOUND
,
)
############################
# UpdateFunctionById
############################
@
router
.
post
(
"/id/{id}/update"
,
response_model
=
Optional
[
FunctionModel
])
async
def
update_function_by_id
(
request
:
Request
,
id
:
str
,
form_data
:
FunctionForm
,
user
=
Depends
(
get_admin_user
)
):
function_path
=
os
.
path
.
join
(
FUNCTIONS_DIR
,
f
"
{
id
}
.py"
)
try
:
with
open
(
function_path
,
"w"
)
as
function_file
:
function_file
.
write
(
form_data
.
content
)
function_module
,
function_type
,
frontmatter
=
load_function_module_by_id
(
id
)
form_data
.
meta
.
manifest
=
frontmatter
FUNCTIONS
=
request
.
app
.
state
.
FUNCTIONS
FUNCTIONS
[
id
]
=
function_module
updated
=
{
**
form_data
.
model_dump
(
exclude
=
{
"id"
}),
"type"
:
function_type
}
print
(
updated
)
function
=
Functions
.
update_function_by_id
(
id
,
updated
)
if
function
:
return
function
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_400_BAD_REQUEST
,
detail
=
ERROR_MESSAGES
.
DEFAULT
(
"Error updating function"
),
)
except
Exception
as
e
:
raise
HTTPException
(
status_code
=
status
.
HTTP_400_BAD_REQUEST
,
detail
=
ERROR_MESSAGES
.
DEFAULT
(
e
),
)
############################
# DeleteFunctionById
############################
@
router
.
delete
(
"/id/{id}/delete"
,
response_model
=
bool
)
async
def
delete_function_by_id
(
request
:
Request
,
id
:
str
,
user
=
Depends
(
get_admin_user
)
):
result
=
Functions
.
delete_function_by_id
(
id
)
if
result
:
FUNCTIONS
=
request
.
app
.
state
.
FUNCTIONS
if
id
in
FUNCTIONS
:
del
FUNCTIONS
[
id
]
# delete the function file
function_path
=
os
.
path
.
join
(
FUNCTIONS_DIR
,
f
"
{
id
}
.py"
)
os
.
remove
(
function_path
)
return
result
############################
# GetFunctionValves
############################
@
router
.
get
(
"/id/{id}/valves"
,
response_model
=
Optional
[
dict
])
async
def
get_function_valves_by_id
(
id
:
str
,
user
=
Depends
(
get_admin_user
)):
function
=
Functions
.
get_function_by_id
(
id
)
if
function
:
try
:
valves
=
Functions
.
get_function_valves_by_id
(
id
)
return
valves
except
Exception
as
e
:
raise
HTTPException
(
status_code
=
status
.
HTTP_400_BAD_REQUEST
,
detail
=
ERROR_MESSAGES
.
DEFAULT
(
e
),
)
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_401_UNAUTHORIZED
,
detail
=
ERROR_MESSAGES
.
NOT_FOUND
,
)
############################
# GetFunctionValvesSpec
############################
@
router
.
get
(
"/id/{id}/valves/spec"
,
response_model
=
Optional
[
dict
])
async
def
get_function_valves_spec_by_id
(
request
:
Request
,
id
:
str
,
user
=
Depends
(
get_admin_user
)
):
function
=
Functions
.
get_function_by_id
(
id
)
if
function
:
if
id
in
request
.
app
.
state
.
FUNCTIONS
:
function_module
=
request
.
app
.
state
.
FUNCTIONS
[
id
]
else
:
function_module
,
function_type
,
frontmatter
=
load_function_module_by_id
(
id
)
request
.
app
.
state
.
FUNCTIONS
[
id
]
=
function_module
if
hasattr
(
function_module
,
"Valves"
):
Valves
=
function_module
.
Valves
return
Valves
.
schema
()
return
None
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_401_UNAUTHORIZED
,
detail
=
ERROR_MESSAGES
.
NOT_FOUND
,
)
############################
# UpdateFunctionValves
############################
@
router
.
post
(
"/id/{id}/valves/update"
,
response_model
=
Optional
[
dict
])
async
def
update_function_valves_by_id
(
request
:
Request
,
id
:
str
,
form_data
:
dict
,
user
=
Depends
(
get_admin_user
)
):
function
=
Functions
.
get_function_by_id
(
id
)
if
function
:
if
id
in
request
.
app
.
state
.
FUNCTIONS
:
function_module
=
request
.
app
.
state
.
FUNCTIONS
[
id
]
else
:
function_module
,
function_type
,
frontmatter
=
load_function_module_by_id
(
id
)
request
.
app
.
state
.
FUNCTIONS
[
id
]
=
function_module
if
hasattr
(
function_module
,
"Valves"
):
Valves
=
function_module
.
Valves
try
:
form_data
=
{
k
:
v
for
k
,
v
in
form_data
.
items
()
if
v
is
not
None
}
valves
=
Valves
(
**
form_data
)
Functions
.
update_function_valves_by_id
(
id
,
valves
.
model_dump
())
return
valves
.
model_dump
()
except
Exception
as
e
:
print
(
e
)
raise
HTTPException
(
status_code
=
status
.
HTTP_400_BAD_REQUEST
,
detail
=
ERROR_MESSAGES
.
DEFAULT
(
e
),
)
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_401_UNAUTHORIZED
,
detail
=
ERROR_MESSAGES
.
NOT_FOUND
,
)
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_401_UNAUTHORIZED
,
detail
=
ERROR_MESSAGES
.
NOT_FOUND
,
)
############################
# FunctionUserValves
############################
@
router
.
get
(
"/id/{id}/valves/user"
,
response_model
=
Optional
[
dict
])
async
def
get_function_user_valves_by_id
(
id
:
str
,
user
=
Depends
(
get_verified_user
)):
function
=
Functions
.
get_function_by_id
(
id
)
if
function
:
try
:
user_valves
=
Functions
.
get_user_valves_by_id_and_user_id
(
id
,
user
.
id
)
return
user_valves
except
Exception
as
e
:
raise
HTTPException
(
status_code
=
status
.
HTTP_400_BAD_REQUEST
,
detail
=
ERROR_MESSAGES
.
DEFAULT
(
e
),
)
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_401_UNAUTHORIZED
,
detail
=
ERROR_MESSAGES
.
NOT_FOUND
,
)
@
router
.
get
(
"/id/{id}/valves/user/spec"
,
response_model
=
Optional
[
dict
])
async
def
get_function_user_valves_spec_by_id
(
request
:
Request
,
id
:
str
,
user
=
Depends
(
get_verified_user
)
):
function
=
Functions
.
get_function_by_id
(
id
)
if
function
:
if
id
in
request
.
app
.
state
.
FUNCTIONS
:
function_module
=
request
.
app
.
state
.
FUNCTIONS
[
id
]
else
:
function_module
,
function_type
,
frontmatter
=
load_function_module_by_id
(
id
)
request
.
app
.
state
.
FUNCTIONS
[
id
]
=
function_module
if
hasattr
(
function_module
,
"UserValves"
):
UserValves
=
function_module
.
UserValves
return
UserValves
.
schema
()
return
None
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_401_UNAUTHORIZED
,
detail
=
ERROR_MESSAGES
.
NOT_FOUND
,
)
@
router
.
post
(
"/id/{id}/valves/user/update"
,
response_model
=
Optional
[
dict
])
async
def
update_function_user_valves_by_id
(
request
:
Request
,
id
:
str
,
form_data
:
dict
,
user
=
Depends
(
get_verified_user
)
):
function
=
Functions
.
get_function_by_id
(
id
)
if
function
:
if
id
in
request
.
app
.
state
.
FUNCTIONS
:
function_module
=
request
.
app
.
state
.
FUNCTIONS
[
id
]
else
:
function_module
,
function_type
,
frontmatter
=
load_function_module_by_id
(
id
)
request
.
app
.
state
.
FUNCTIONS
[
id
]
=
function_module
if
hasattr
(
function_module
,
"UserValves"
):
UserValves
=
function_module
.
UserValves
try
:
form_data
=
{
k
:
v
for
k
,
v
in
form_data
.
items
()
if
v
is
not
None
}
user_valves
=
UserValves
(
**
form_data
)
Functions
.
update_user_valves_by_id_and_user_id
(
id
,
user
.
id
,
user_valves
.
model_dump
()
)
return
user_valves
.
model_dump
()
except
Exception
as
e
:
print
(
e
)
raise
HTTPException
(
status_code
=
status
.
HTTP_400_BAD_REQUEST
,
detail
=
ERROR_MESSAGES
.
DEFAULT
(
e
),
)
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_401_UNAUTHORIZED
,
detail
=
ERROR_MESSAGES
.
NOT_FOUND
,
)
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_401_UNAUTHORIZED
,
detail
=
ERROR_MESSAGES
.
NOT_FOUND
,
)
backend/apps/webui/routers/memories.py
View file @
1eebb85f
...
...
@@ -101,6 +101,7 @@ async def update_memory_by_id(
class
QueryMemoryForm
(
BaseModel
):
content
:
str
k
:
Optional
[
int
]
=
1
@
router
.
post
(
"/query"
)
...
...
@@ -112,7 +113,7 @@ async def query_memory(
results
=
collection
.
query
(
query_embeddings
=
[
query_embedding
],
n_results
=
1
,
# how many results to return
n_results
=
form_data
.
k
,
# how many results to return
)
return
results
...
...
backend/apps/webui/routers/prompts.py
View file @
1eebb85f
...
...
@@ -8,7 +8,7 @@ import json
from
apps.webui.models.prompts
import
Prompts
,
PromptForm
,
PromptModel
from
utils.utils
import
get_
current
_user
,
get_admin_user
from
utils.utils
import
get_
verified
_user
,
get_admin_user
from
constants
import
ERROR_MESSAGES
router
=
APIRouter
()
...
...
@@ -19,7 +19,7 @@ router = APIRouter()
@
router
.
get
(
"/"
,
response_model
=
List
[
PromptModel
])
async
def
get_prompts
(
user
=
Depends
(
get_
current
_user
)):
async
def
get_prompts
(
user
=
Depends
(
get_
verified
_user
)):
return
Prompts
.
get_prompts
()
...
...
@@ -52,7 +52,7 @@ async def create_new_prompt(form_data: PromptForm, user=Depends(get_admin_user))
@
router
.
get
(
"/command/{command}"
,
response_model
=
Optional
[
PromptModel
])
async
def
get_prompt_by_command
(
command
:
str
,
user
=
Depends
(
get_
current
_user
)):
async
def
get_prompt_by_command
(
command
:
str
,
user
=
Depends
(
get_
verified
_user
)):
prompt
=
Prompts
.
get_prompt_by_command
(
f
"/
{
command
}
"
)
if
prompt
:
...
...
backend/apps/webui/routers/tools.py
View file @
1eebb85f
...
...
@@ -6,17 +6,20 @@ from fastapi import APIRouter
from
pydantic
import
BaseModel
import
json
from
apps.webui.models.users
import
Users
from
apps.webui.models.tools
import
Tools
,
ToolForm
,
ToolModel
,
ToolResponse
from
apps.webui.utils
import
load_toolkit_module_by_id
from
utils.utils
import
get_
current
_user
,
get_
admin
_user
from
utils.utils
import
get_
admin
_user
,
get_
verified
_user
from
utils.tools
import
get_tools_specs
from
constants
import
ERROR_MESSAGES
from
importlib
import
util
import
os
from
pathlib
import
Path
from
config
import
DATA_DIR
from
config
import
DATA_DIR
,
CACHE_DIR
TOOLS_DIR
=
f
"
{
DATA_DIR
}
/tools"
...
...
@@ -31,7 +34,7 @@ router = APIRouter()
@
router
.
get
(
"/"
,
response_model
=
List
[
ToolResponse
])
async
def
get_toolkits
(
user
=
Depends
(
get_
current
_user
)):
async
def
get_toolkits
(
user
=
Depends
(
get_
verified
_user
)):
toolkits
=
[
toolkit
for
toolkit
in
Tools
.
get_tools
()]
return
toolkits
...
...
@@ -71,7 +74,8 @@ async def create_new_toolkit(
with
open
(
toolkit_path
,
"w"
)
as
tool_file
:
tool_file
.
write
(
form_data
.
content
)
toolkit_module
=
load_toolkit_module_by_id
(
form_data
.
id
)
toolkit_module
,
frontmatter
=
load_toolkit_module_by_id
(
form_data
.
id
)
form_data
.
meta
.
manifest
=
frontmatter
TOOLS
=
request
.
app
.
state
.
TOOLS
TOOLS
[
form_data
.
id
]
=
toolkit_module
...
...
@@ -79,6 +83,9 @@ async def create_new_toolkit(
specs
=
get_tools_specs
(
TOOLS
[
form_data
.
id
])
toolkit
=
Tools
.
insert_new_tool
(
user
.
id
,
form_data
,
specs
)
tool_cache_dir
=
Path
(
CACHE_DIR
)
/
"tools"
/
form_data
.
id
tool_cache_dir
.
mkdir
(
parents
=
True
,
exist_ok
=
True
)
if
toolkit
:
return
toolkit
else
:
...
...
@@ -132,7 +139,8 @@ async def update_toolkit_by_id(
with
open
(
toolkit_path
,
"w"
)
as
tool_file
:
tool_file
.
write
(
form_data
.
content
)
toolkit_module
=
load_toolkit_module_by_id
(
id
)
toolkit_module
,
frontmatter
=
load_toolkit_module_by_id
(
id
)
form_data
.
meta
.
manifest
=
frontmatter
TOOLS
=
request
.
app
.
state
.
TOOLS
TOOLS
[
id
]
=
toolkit_module
...
...
@@ -181,3 +189,187 @@ async def delete_toolkit_by_id(request: Request, id: str, user=Depends(get_admin
os
.
remove
(
toolkit_path
)
return
result
############################
# GetToolValves
############################
@
router
.
get
(
"/id/{id}/valves"
,
response_model
=
Optional
[
dict
])
async
def
get_toolkit_valves_by_id
(
id
:
str
,
user
=
Depends
(
get_admin_user
)):
toolkit
=
Tools
.
get_tool_by_id
(
id
)
if
toolkit
:
try
:
valves
=
Tools
.
get_tool_valves_by_id
(
id
)
return
valves
except
Exception
as
e
:
raise
HTTPException
(
status_code
=
status
.
HTTP_400_BAD_REQUEST
,
detail
=
ERROR_MESSAGES
.
DEFAULT
(
e
),
)
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_401_UNAUTHORIZED
,
detail
=
ERROR_MESSAGES
.
NOT_FOUND
,
)
############################
# GetToolValvesSpec
############################
@
router
.
get
(
"/id/{id}/valves/spec"
,
response_model
=
Optional
[
dict
])
async
def
get_toolkit_valves_spec_by_id
(
request
:
Request
,
id
:
str
,
user
=
Depends
(
get_admin_user
)
):
toolkit
=
Tools
.
get_tool_by_id
(
id
)
if
toolkit
:
if
id
in
request
.
app
.
state
.
TOOLS
:
toolkit_module
=
request
.
app
.
state
.
TOOLS
[
id
]
else
:
toolkit_module
,
frontmatter
=
load_toolkit_module_by_id
(
id
)
request
.
app
.
state
.
TOOLS
[
id
]
=
toolkit_module
if
hasattr
(
toolkit_module
,
"Valves"
):
Valves
=
toolkit_module
.
Valves
return
Valves
.
schema
()
return
None
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_401_UNAUTHORIZED
,
detail
=
ERROR_MESSAGES
.
NOT_FOUND
,
)
############################
# UpdateToolValves
############################
@
router
.
post
(
"/id/{id}/valves/update"
,
response_model
=
Optional
[
dict
])
async
def
update_toolkit_valves_by_id
(
request
:
Request
,
id
:
str
,
form_data
:
dict
,
user
=
Depends
(
get_admin_user
)
):
toolkit
=
Tools
.
get_tool_by_id
(
id
)
if
toolkit
:
if
id
in
request
.
app
.
state
.
TOOLS
:
toolkit_module
=
request
.
app
.
state
.
TOOLS
[
id
]
else
:
toolkit_module
,
frontmatter
=
load_toolkit_module_by_id
(
id
)
request
.
app
.
state
.
TOOLS
[
id
]
=
toolkit_module
if
hasattr
(
toolkit_module
,
"Valves"
):
Valves
=
toolkit_module
.
Valves
try
:
form_data
=
{
k
:
v
for
k
,
v
in
form_data
.
items
()
if
v
is
not
None
}
valves
=
Valves
(
**
form_data
)
Tools
.
update_tool_valves_by_id
(
id
,
valves
.
model_dump
())
return
valves
.
model_dump
()
except
Exception
as
e
:
print
(
e
)
raise
HTTPException
(
status_code
=
status
.
HTTP_400_BAD_REQUEST
,
detail
=
ERROR_MESSAGES
.
DEFAULT
(
e
),
)
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_401_UNAUTHORIZED
,
detail
=
ERROR_MESSAGES
.
NOT_FOUND
,
)
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_401_UNAUTHORIZED
,
detail
=
ERROR_MESSAGES
.
NOT_FOUND
,
)
############################
# ToolUserValves
############################
@
router
.
get
(
"/id/{id}/valves/user"
,
response_model
=
Optional
[
dict
])
async
def
get_toolkit_user_valves_by_id
(
id
:
str
,
user
=
Depends
(
get_verified_user
)):
toolkit
=
Tools
.
get_tool_by_id
(
id
)
if
toolkit
:
try
:
user_valves
=
Tools
.
get_user_valves_by_id_and_user_id
(
id
,
user
.
id
)
return
user_valves
except
Exception
as
e
:
raise
HTTPException
(
status_code
=
status
.
HTTP_400_BAD_REQUEST
,
detail
=
ERROR_MESSAGES
.
DEFAULT
(
e
),
)
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_401_UNAUTHORIZED
,
detail
=
ERROR_MESSAGES
.
NOT_FOUND
,
)
@
router
.
get
(
"/id/{id}/valves/user/spec"
,
response_model
=
Optional
[
dict
])
async
def
get_toolkit_user_valves_spec_by_id
(
request
:
Request
,
id
:
str
,
user
=
Depends
(
get_verified_user
)
):
toolkit
=
Tools
.
get_tool_by_id
(
id
)
if
toolkit
:
if
id
in
request
.
app
.
state
.
TOOLS
:
toolkit_module
=
request
.
app
.
state
.
TOOLS
[
id
]
else
:
toolkit_module
,
frontmatter
=
load_toolkit_module_by_id
(
id
)
request
.
app
.
state
.
TOOLS
[
id
]
=
toolkit_module
if
hasattr
(
toolkit_module
,
"UserValves"
):
UserValves
=
toolkit_module
.
UserValves
return
UserValves
.
schema
()
return
None
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_401_UNAUTHORIZED
,
detail
=
ERROR_MESSAGES
.
NOT_FOUND
,
)
@
router
.
post
(
"/id/{id}/valves/user/update"
,
response_model
=
Optional
[
dict
])
async
def
update_toolkit_user_valves_by_id
(
request
:
Request
,
id
:
str
,
form_data
:
dict
,
user
=
Depends
(
get_verified_user
)
):
toolkit
=
Tools
.
get_tool_by_id
(
id
)
if
toolkit
:
if
id
in
request
.
app
.
state
.
TOOLS
:
toolkit_module
=
request
.
app
.
state
.
TOOLS
[
id
]
else
:
toolkit_module
,
frontmatter
=
load_toolkit_module_by_id
(
id
)
request
.
app
.
state
.
TOOLS
[
id
]
=
toolkit_module
if
hasattr
(
toolkit_module
,
"UserValves"
):
UserValves
=
toolkit_module
.
UserValves
try
:
form_data
=
{
k
:
v
for
k
,
v
in
form_data
.
items
()
if
v
is
not
None
}
user_valves
=
UserValves
(
**
form_data
)
Tools
.
update_user_valves_by_id_and_user_id
(
id
,
user
.
id
,
user_valves
.
model_dump
()
)
return
user_valves
.
model_dump
()
except
Exception
as
e
:
print
(
e
)
raise
HTTPException
(
status_code
=
status
.
HTTP_400_BAD_REQUEST
,
detail
=
ERROR_MESSAGES
.
DEFAULT
(
e
),
)
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_401_UNAUTHORIZED
,
detail
=
ERROR_MESSAGES
.
NOT_FOUND
,
)
else
:
raise
HTTPException
(
status_code
=
status
.
HTTP_401_UNAUTHORIZED
,
detail
=
ERROR_MESSAGES
.
NOT_FOUND
,
)
backend/apps/webui/utils.py
View file @
1eebb85f
from
importlib
import
util
import
os
import
re
from
config
import
TOOLS_DIR
from
config
import
TOOLS_DIR
,
FUNCTIONS_DIR
def
extract_frontmatter
(
file_path
):
"""
Extract frontmatter as a dictionary from the specified file path.
"""
frontmatter
=
{}
frontmatter_started
=
False
frontmatter_ended
=
False
frontmatter_pattern
=
re
.
compile
(
r
"^\s*([a-z_]+):\s*(.*)\s*$"
,
re
.
IGNORECASE
)
try
:
with
open
(
file_path
,
"r"
,
encoding
=
"utf-8"
)
as
file
:
first_line
=
file
.
readline
()
if
first_line
.
strip
()
!=
'"""'
:
# The file doesn't start with triple quotes
return
{}
frontmatter_started
=
True
for
line
in
file
:
if
'"""'
in
line
:
if
frontmatter_started
:
frontmatter_ended
=
True
break
if
frontmatter_started
and
not
frontmatter_ended
:
match
=
frontmatter_pattern
.
match
(
line
)
if
match
:
key
,
value
=
match
.
groups
()
frontmatter
[
key
.
strip
()]
=
value
.
strip
()
except
FileNotFoundError
:
print
(
f
"Error: The file
{
file_path
}
does not exist."
)
return
{}
except
Exception
as
e
:
print
(
f
"An error occurred:
{
e
}
"
)
return
{}
return
frontmatter
def
load_toolkit_module_by_id
(
toolkit_id
):
toolkit_path
=
os
.
path
.
join
(
TOOLS_DIR
,
f
"
{
toolkit_id
}
.py"
)
spec
=
util
.
spec_from_file_location
(
toolkit_id
,
toolkit_path
)
module
=
util
.
module_from_spec
(
spec
)
frontmatter
=
extract_frontmatter
(
toolkit_path
)
try
:
spec
.
loader
.
exec_module
(
module
)
print
(
f
"Loaded module:
{
module
.
__name__
}
"
)
if
hasattr
(
module
,
"Tools"
):
return
module
.
Tools
()
return
module
.
Tools
()
,
frontmatter
else
:
raise
Exception
(
"No Tools class found"
)
except
Exception
as
e
:
...
...
@@ -21,3 +63,26 @@ def load_toolkit_module_by_id(toolkit_id):
# Move the file to the error folder
os
.
rename
(
toolkit_path
,
f
"
{
toolkit_path
}
.error"
)
raise
e
def
load_function_module_by_id
(
function_id
):
function_path
=
os
.
path
.
join
(
FUNCTIONS_DIR
,
f
"
{
function_id
}
.py"
)
spec
=
util
.
spec_from_file_location
(
function_id
,
function_path
)
module
=
util
.
module_from_spec
(
spec
)
frontmatter
=
extract_frontmatter
(
function_path
)
try
:
spec
.
loader
.
exec_module
(
module
)
print
(
f
"Loaded module:
{
module
.
__name__
}
"
)
if
hasattr
(
module
,
"Pipe"
):
return
module
.
Pipe
(),
"pipe"
,
frontmatter
elif
hasattr
(
module
,
"Filter"
):
return
module
.
Filter
(),
"filter"
,
frontmatter
else
:
raise
Exception
(
"No Function class found"
)
except
Exception
as
e
:
print
(
f
"Error loading module:
{
function_id
}
"
)
# Move the file to the error folder
os
.
rename
(
function_path
,
f
"
{
function_path
}
.error"
)
raise
e
Prev
1
2
3
4
5
6
…
9
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