Unverified Commit 3b0cb794 authored by Timothy Jaeryang Baek's avatar Timothy Jaeryang Baek Committed by GitHub
Browse files

Merge pull request #1362 from open-webui/dev

0.1.116
parents ac294a74 46a18104
...@@ -25,3 +25,5 @@ jobs: ...@@ -25,3 +25,5 @@ jobs:
pip install yapf pip install yapf
- name: Format backend - name: Format backend
run: bun run format:backend run: bun run format:backend
- name: Check for changes after format
run: git diff --exit-code
...@@ -18,5 +18,8 @@ jobs: ...@@ -18,5 +18,8 @@ jobs:
run: bun install run: bun install
- name: Format frontend - name: Format frontend
run: bun run format run: bun run format
- name: Check for changes after format
run: git diff --exit-code
- name: Build frontend - name: Build frontend
if: always()
run: bun run build run: bun run build
...@@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file. ...@@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.1.116] - 2024-03-31
### Added
- **🔄 Enhanced UI**: Model selector now conveniently located in the navbar, enabling seamless switching between multiple models during conversations.
- **🔍 Improved Model Selector**: Directly pull a model from the selector/Models now display detailed information for better understanding.
- **💬 Webhook Support**: Now compatible with Google Chat and Microsoft Teams.
- **🌐 Localization**: Korean translation (I18n) now available.
- **🌑 Dark Theme**: OLED dark theme introduced for reduced strain during prolonged usage.
- **🏷️ Tag Autocomplete**: Dropdown feature added for effortless chat tagging.
### Fixed
- **🔽 Auto-Scrolling**: Addressed OpenAI auto-scrolling issue.
- **🏷️ Tag Validation**: Implemented tag validation to prevent empty string tags.
- **🚫 Model Whitelisting**: Resolved LiteLLM model whitelisting issue.
- **✅ Spelling**: Corrected various spelling issues for improved readability.
## [0.1.115] - 2024-03-24 ## [0.1.115] - 2024-03-24
### Added ### Added
......
...@@ -22,7 +22,13 @@ from utils.utils import ( ...@@ -22,7 +22,13 @@ from utils.utils import (
) )
from utils.misc import calculate_sha256 from utils.misc import calculate_sha256
from config import SRC_LOG_LEVELS, CACHE_DIR, UPLOAD_DIR, WHISPER_MODEL, WHISPER_MODEL_DIR from config import (
SRC_LOG_LEVELS,
CACHE_DIR,
UPLOAD_DIR,
WHISPER_MODEL,
WHISPER_MODEL_DIR,
)
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["AUDIO"]) log.setLevel(SRC_LOG_LEVELS["AUDIO"])
......
...@@ -33,7 +33,13 @@ from constants import ERROR_MESSAGES ...@@ -33,7 +33,13 @@ from constants import ERROR_MESSAGES
from utils.utils import decode_token, get_current_user, get_admin_user from utils.utils import decode_token, get_current_user, get_admin_user
from config import SRC_LOG_LEVELS, OLLAMA_BASE_URLS, MODEL_FILTER_ENABLED, MODEL_FILTER_LIST, UPLOAD_DIR from config import (
SRC_LOG_LEVELS,
OLLAMA_BASE_URLS,
MODEL_FILTER_ENABLED,
MODEL_FILTER_LIST,
UPLOAD_DIR,
)
from utils.misc import calculate_sha256 from utils.misc import calculate_sha256
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -770,7 +776,11 @@ async def generate_chat_completion( ...@@ -770,7 +776,11 @@ async def generate_chat_completion(
r = None r = None
log.debug("form_data.model_dump_json(exclude_none=True).encode(): {0} ".format(form_data.model_dump_json(exclude_none=True).encode())) log.debug(
"form_data.model_dump_json(exclude_none=True).encode(): {0} ".format(
form_data.model_dump_json(exclude_none=True).encode()
)
)
def get_request(): def get_request():
nonlocal form_data nonlocal form_data
......
...@@ -21,6 +21,7 @@ from langchain_community.document_loaders import ( ...@@ -21,6 +21,7 @@ from langchain_community.document_loaders import (
TextLoader, TextLoader,
PyPDFLoader, PyPDFLoader,
CSVLoader, CSVLoader,
BSHTMLLoader,
Docx2txtLoader, Docx2txtLoader,
UnstructuredEPubLoader, UnstructuredEPubLoader,
UnstructuredWordDocumentLoader, UnstructuredWordDocumentLoader,
...@@ -114,6 +115,7 @@ class CollectionNameForm(BaseModel): ...@@ -114,6 +115,7 @@ class CollectionNameForm(BaseModel):
class StoreWebForm(CollectionNameForm): class StoreWebForm(CollectionNameForm):
url: str url: str
@app.get("/") @app.get("/")
async def get_status(): async def get_status():
return { return {
...@@ -296,13 +298,18 @@ def store_web(form_data: StoreWebForm, user=Depends(get_current_user)): ...@@ -296,13 +298,18 @@ def store_web(form_data: StoreWebForm, user=Depends(get_current_user)):
def store_data_in_vector_db(data, collection_name, overwrite: bool = False) -> bool: def store_data_in_vector_db(data, collection_name, overwrite: bool = False) -> bool:
text_splitter = RecursiveCharacterTextSplitter( text_splitter = RecursiveCharacterTextSplitter(
chunk_size=app.state.CHUNK_SIZE, chunk_size=app.state.CHUNK_SIZE,
chunk_overlap=app.state.CHUNK_OVERLAP, chunk_overlap=app.state.CHUNK_OVERLAP,
add_start_index=True, add_start_index=True,
) )
docs = text_splitter.split_documents(data) docs = text_splitter.split_documents(data)
return store_docs_in_vector_db(docs, collection_name, overwrite)
if len(docs) > 0:
return store_docs_in_vector_db(docs, collection_name, overwrite), None
else:
raise ValueError(ERROR_MESSAGES.EMPTY_CONTENT)
def store_text_in_vector_db( def store_text_in_vector_db(
...@@ -318,6 +325,7 @@ def store_text_in_vector_db( ...@@ -318,6 +325,7 @@ def store_text_in_vector_db(
def store_docs_in_vector_db(docs, collection_name, overwrite: bool = False) -> bool: def store_docs_in_vector_db(docs, collection_name, overwrite: bool = False) -> bool:
texts = [doc.page_content for doc in docs] texts = [doc.page_content for doc in docs]
metadatas = [doc.metadata for doc in docs] metadatas = [doc.metadata for doc in docs]
...@@ -402,6 +410,8 @@ def get_loader(filename: str, file_content_type: str, file_path: str): ...@@ -402,6 +410,8 @@ def get_loader(filename: str, file_content_type: str, file_path: str):
loader = UnstructuredRSTLoader(file_path, mode="elements") loader = UnstructuredRSTLoader(file_path, mode="elements")
elif file_ext == "xml": elif file_ext == "xml":
loader = UnstructuredXMLLoader(file_path) loader = UnstructuredXMLLoader(file_path)
elif file_ext in ["htm", "html"]:
loader = BSHTMLLoader(file_path, open_encoding="unicode_escape")
elif file_ext == "md": elif file_ext == "md":
loader = UnstructuredMarkdownLoader(file_path) loader = UnstructuredMarkdownLoader(file_path)
elif file_content_type == "application/epub+zip": elif file_content_type == "application/epub+zip":
...@@ -452,6 +462,8 @@ def store_doc( ...@@ -452,6 +462,8 @@ def store_doc(
loader, known_type = get_loader(file.filename, file.content_type, file_path) loader, known_type = get_loader(file.filename, file.content_type, file_path)
data = loader.load() data = loader.load()
try:
result = store_data_in_vector_db(data, collection_name) result = store_data_in_vector_db(data, collection_name)
if result: if result:
...@@ -461,10 +473,10 @@ def store_doc( ...@@ -461,10 +473,10 @@ def store_doc(
"filename": filename, "filename": filename,
"known_type": known_type, "known_type": known_type,
} }
else: except Exception as e:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=ERROR_MESSAGES.DEFAULT(), detail=e,
) )
except Exception as e: except Exception as e:
log.exception(e) log.exception(e)
...@@ -529,6 +541,7 @@ def scan_docs_dir(user=Depends(get_admin_user)): ...@@ -529,6 +541,7 @@ def scan_docs_dir(user=Depends(get_admin_user)):
) )
data = loader.load() data = loader.load()
try:
result = store_data_in_vector_db(data, collection_name) result = store_data_in_vector_db(data, collection_name)
if result: if result:
...@@ -561,6 +574,9 @@ def scan_docs_dir(user=Depends(get_admin_user)): ...@@ -561,6 +574,9 @@ def scan_docs_dir(user=Depends(get_admin_user)):
} }
), ),
) )
except Exception as e:
print(e)
pass
except Exception as e: except Exception as e:
log.exception(e) log.exception(e)
......
...@@ -11,6 +11,7 @@ from utils.utils import verify_password ...@@ -11,6 +11,7 @@ from utils.utils import verify_password
from apps.web.internal.db import DB from apps.web.internal.db import DB
from config import SRC_LOG_LEVELS from config import SRC_LOG_LEVELS
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["MODELS"]) log.setLevel(SRC_LOG_LEVELS["MODELS"])
......
...@@ -13,6 +13,7 @@ from apps.web.internal.db import DB ...@@ -13,6 +13,7 @@ from apps.web.internal.db import DB
import json import json
from config import SRC_LOG_LEVELS from config import SRC_LOG_LEVELS
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["MODELS"]) log.setLevel(SRC_LOG_LEVELS["MODELS"])
......
...@@ -64,8 +64,8 @@ class ModelfilesTable: ...@@ -64,8 +64,8 @@ class ModelfilesTable:
self.db.create_tables([Modelfile]) self.db.create_tables([Modelfile])
def insert_new_modelfile( def insert_new_modelfile(
self, user_id: str, self, user_id: str, form_data: ModelfileForm
form_data: ModelfileForm) -> Optional[ModelfileModel]: ) -> Optional[ModelfileModel]:
if "tagName" in form_data.modelfile: if "tagName" in form_data.modelfile:
modelfile = ModelfileModel( modelfile = ModelfileModel(
**{ **{
...@@ -73,7 +73,8 @@ class ModelfilesTable: ...@@ -73,7 +73,8 @@ class ModelfilesTable:
"tag_name": form_data.modelfile["tagName"], "tag_name": form_data.modelfile["tagName"],
"modelfile": json.dumps(form_data.modelfile), "modelfile": json.dumps(form_data.modelfile),
"timestamp": int(time.time()), "timestamp": int(time.time()),
}) }
)
try: try:
result = Modelfile.create(**modelfile.model_dump()) result = Modelfile.create(**modelfile.model_dump())
...@@ -87,29 +88,28 @@ class ModelfilesTable: ...@@ -87,29 +88,28 @@ class ModelfilesTable:
else: else:
return None return None
def get_modelfile_by_tag_name(self, def get_modelfile_by_tag_name(self, tag_name: str) -> Optional[ModelfileModel]:
tag_name: str) -> Optional[ModelfileModel]:
try: try:
modelfile = Modelfile.get(Modelfile.tag_name == tag_name) modelfile = Modelfile.get(Modelfile.tag_name == tag_name)
return ModelfileModel(**model_to_dict(modelfile)) return ModelfileModel(**model_to_dict(modelfile))
except: except:
return None return None
def get_modelfiles(self, def get_modelfiles(self, skip: int = 0, limit: int = 50) -> List[ModelfileResponse]:
skip: int = 0,
limit: int = 50) -> List[ModelfileResponse]:
return [ return [
ModelfileResponse( ModelfileResponse(
**{ **{
**model_to_dict(modelfile), **model_to_dict(modelfile),
"modelfile": "modelfile": json.loads(modelfile.modelfile),
json.loads(modelfile.modelfile), }
}) for modelfile in Modelfile.select() )
for modelfile in Modelfile.select()
# .limit(limit).offset(skip) # .limit(limit).offset(skip)
] ]
def update_modelfile_by_tag_name( def update_modelfile_by_tag_name(
self, tag_name: str, modelfile: dict) -> Optional[ModelfileModel]: self, tag_name: str, modelfile: dict
) -> Optional[ModelfileModel]:
try: try:
query = Modelfile.update( query = Modelfile.update(
modelfile=json.dumps(modelfile), modelfile=json.dumps(modelfile),
......
...@@ -52,8 +52,9 @@ class PromptsTable: ...@@ -52,8 +52,9 @@ class PromptsTable:
self.db = db self.db = db
self.db.create_tables([Prompt]) self.db.create_tables([Prompt])
def insert_new_prompt(self, user_id: str, def insert_new_prompt(
form_data: PromptForm) -> Optional[PromptModel]: self, user_id: str, form_data: PromptForm
) -> Optional[PromptModel]:
prompt = PromptModel( prompt = PromptModel(
**{ **{
"user_id": user_id, "user_id": user_id,
...@@ -61,7 +62,8 @@ class PromptsTable: ...@@ -61,7 +62,8 @@ class PromptsTable:
"title": form_data.title, "title": form_data.title,
"content": form_data.content, "content": form_data.content,
"timestamp": int(time.time()), "timestamp": int(time.time()),
}) }
)
try: try:
result = Prompt.create(**prompt.model_dump()) result = Prompt.create(**prompt.model_dump())
...@@ -81,13 +83,14 @@ class PromptsTable: ...@@ -81,13 +83,14 @@ class PromptsTable:
def get_prompts(self) -> List[PromptModel]: def get_prompts(self) -> List[PromptModel]:
return [ return [
PromptModel(**model_to_dict(prompt)) for prompt in Prompt.select() PromptModel(**model_to_dict(prompt))
for prompt in Prompt.select()
# .limit(limit).offset(skip) # .limit(limit).offset(skip)
] ]
def update_prompt_by_command( def update_prompt_by_command(
self, command: str, self, command: str, form_data: PromptForm
form_data: PromptForm) -> Optional[PromptModel]: ) -> Optional[PromptModel]:
try: try:
query = Prompt.update( query = Prompt.update(
title=form_data.title, title=form_data.title,
......
...@@ -11,6 +11,7 @@ import logging ...@@ -11,6 +11,7 @@ import logging
from apps.web.internal.db import DB from apps.web.internal.db import DB
from config import SRC_LOG_LEVELS from config import SRC_LOG_LEVELS
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["MODELS"]) log.setLevel(SRC_LOG_LEVELS["MODELS"])
......
...@@ -29,6 +29,7 @@ from apps.web.models.tags import ( ...@@ -29,6 +29,7 @@ from apps.web.models.tags import (
from constants import ERROR_MESSAGES from constants import ERROR_MESSAGES
from config import SRC_LOG_LEVELS from config import SRC_LOG_LEVELS
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["MODELS"]) log.setLevel(SRC_LOG_LEVELS["MODELS"])
......
...@@ -10,7 +10,12 @@ import uuid ...@@ -10,7 +10,12 @@ import uuid
from apps.web.models.users import Users from apps.web.models.users import Users
from utils.utils import get_password_hash, get_current_user, get_admin_user, create_token from utils.utils import (
get_password_hash,
get_current_user,
get_admin_user,
create_token,
)
from utils.misc import get_gravatar_url, validate_email_format from utils.misc import get_gravatar_url, validate_email_format
from constants import ERROR_MESSAGES from constants import ERROR_MESSAGES
...@@ -43,7 +48,6 @@ async def set_global_default_models( ...@@ -43,7 +48,6 @@ async def set_global_default_models(
return request.app.state.DEFAULT_MODELS return request.app.state.DEFAULT_MODELS
@router.post("/default/suggestions", response_model=List[PromptSuggestion]) @router.post("/default/suggestions", response_model=List[PromptSuggestion])
async def set_global_default_suggestions( async def set_global_default_suggestions(
request: Request, request: Request,
......
...@@ -24,9 +24,9 @@ router = APIRouter() ...@@ -24,9 +24,9 @@ router = APIRouter()
@router.get("/", response_model=List[ModelfileResponse]) @router.get("/", response_model=List[ModelfileResponse])
async def get_modelfiles(skip: int = 0, async def get_modelfiles(
limit: int = 50, skip: int = 0, limit: int = 50, user=Depends(get_current_user)
user=Depends(get_current_user)): ):
return Modelfiles.get_modelfiles(skip, limit) return Modelfiles.get_modelfiles(skip, limit)
...@@ -36,17 +36,16 @@ async def get_modelfiles(skip: int = 0, ...@@ -36,17 +36,16 @@ async def get_modelfiles(skip: int = 0,
@router.post("/create", response_model=Optional[ModelfileResponse]) @router.post("/create", response_model=Optional[ModelfileResponse])
async def create_new_modelfile(form_data: ModelfileForm, async def create_new_modelfile(form_data: ModelfileForm, user=Depends(get_admin_user)):
user=Depends(get_admin_user)):
modelfile = Modelfiles.insert_new_modelfile(user.id, form_data) modelfile = Modelfiles.insert_new_modelfile(user.id, form_data)
if modelfile: if modelfile:
return ModelfileResponse( return ModelfileResponse(
**{ **{
**modelfile.model_dump(), **modelfile.model_dump(),
"modelfile": "modelfile": json.loads(modelfile.modelfile),
json.loads(modelfile.modelfile), }
}) )
else: else:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, status_code=status.HTTP_401_UNAUTHORIZED,
...@@ -60,17 +59,18 @@ async def create_new_modelfile(form_data: ModelfileForm, ...@@ -60,17 +59,18 @@ async def create_new_modelfile(form_data: ModelfileForm,
@router.post("/", response_model=Optional[ModelfileResponse]) @router.post("/", response_model=Optional[ModelfileResponse])
async def get_modelfile_by_tag_name(form_data: ModelfileTagNameForm, async def get_modelfile_by_tag_name(
user=Depends(get_current_user)): form_data: ModelfileTagNameForm, user=Depends(get_current_user)
):
modelfile = Modelfiles.get_modelfile_by_tag_name(form_data.tag_name) modelfile = Modelfiles.get_modelfile_by_tag_name(form_data.tag_name)
if modelfile: if modelfile:
return ModelfileResponse( return ModelfileResponse(
**{ **{
**modelfile.model_dump(), **modelfile.model_dump(),
"modelfile": "modelfile": json.loads(modelfile.modelfile),
json.loads(modelfile.modelfile), }
}) )
else: else:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, status_code=status.HTTP_401_UNAUTHORIZED,
...@@ -84,8 +84,9 @@ async def get_modelfile_by_tag_name(form_data: ModelfileTagNameForm, ...@@ -84,8 +84,9 @@ async def get_modelfile_by_tag_name(form_data: ModelfileTagNameForm,
@router.post("/update", response_model=Optional[ModelfileResponse]) @router.post("/update", response_model=Optional[ModelfileResponse])
async def update_modelfile_by_tag_name(form_data: ModelfileUpdateForm, async def update_modelfile_by_tag_name(
user=Depends(get_admin_user)): form_data: ModelfileUpdateForm, user=Depends(get_admin_user)
):
modelfile = Modelfiles.get_modelfile_by_tag_name(form_data.tag_name) modelfile = Modelfiles.get_modelfile_by_tag_name(form_data.tag_name)
if modelfile: if modelfile:
updated_modelfile = { updated_modelfile = {
...@@ -94,14 +95,15 @@ async def update_modelfile_by_tag_name(form_data: ModelfileUpdateForm, ...@@ -94,14 +95,15 @@ async def update_modelfile_by_tag_name(form_data: ModelfileUpdateForm,
} }
modelfile = Modelfiles.update_modelfile_by_tag_name( modelfile = Modelfiles.update_modelfile_by_tag_name(
form_data.tag_name, updated_modelfile) form_data.tag_name, updated_modelfile
)
return ModelfileResponse( return ModelfileResponse(
**{ **{
**modelfile.model_dump(), **modelfile.model_dump(),
"modelfile": "modelfile": json.loads(modelfile.modelfile),
json.loads(modelfile.modelfile), }
}) )
else: else:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, status_code=status.HTTP_401_UNAUTHORIZED,
...@@ -115,7 +117,8 @@ async def update_modelfile_by_tag_name(form_data: ModelfileUpdateForm, ...@@ -115,7 +117,8 @@ async def update_modelfile_by_tag_name(form_data: ModelfileUpdateForm,
@router.delete("/delete", response_model=bool) @router.delete("/delete", response_model=bool)
async def delete_modelfile_by_tag_name(form_data: ModelfileTagNameForm, async def delete_modelfile_by_tag_name(
user=Depends(get_admin_user)): form_data: ModelfileTagNameForm, user=Depends(get_admin_user)
):
result = Modelfiles.delete_modelfile_by_tag_name(form_data.tag_name) result = Modelfiles.delete_modelfile_by_tag_name(form_data.tag_name)
return result return result
...@@ -16,6 +16,7 @@ from utils.utils import get_current_user, get_password_hash, get_admin_user ...@@ -16,6 +16,7 @@ from utils.utils import get_current_user, get_password_hash, get_admin_user
from constants import ERROR_MESSAGES from constants import ERROR_MESSAGES
from config import SRC_LOG_LEVELS from config import SRC_LOG_LEVELS
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["MODELS"]) log.setLevel(SRC_LOG_LEVELS["MODELS"])
......
...@@ -26,6 +26,7 @@ except ImportError: ...@@ -26,6 +26,7 @@ except ImportError:
log.warning("dotenv not installed, skipping...") log.warning("dotenv not installed, skipping...")
WEBUI_NAME = "Open WebUI" WEBUI_NAME = "Open WebUI"
WEBUI_FAVICON_URL = "https://openwebui.com/favicon.png"
shutil.copyfile("../build/favicon.png", "./static/favicon.png") shutil.copyfile("../build/favicon.png", "./static/favicon.png")
#################################### ####################################
...@@ -116,7 +117,18 @@ else: ...@@ -116,7 +117,18 @@ else:
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
log.info(f"GLOBAL_LOG_LEVEL: {GLOBAL_LOG_LEVEL}") log.info(f"GLOBAL_LOG_LEVEL: {GLOBAL_LOG_LEVEL}")
log_sources = ["AUDIO", "CONFIG", "DB", "IMAGES", "LITELLM", "MAIN", "MODELS", "OLLAMA", "OPENAI", "RAG"] log_sources = [
"AUDIO",
"CONFIG",
"DB",
"IMAGES",
"LITELLM",
"MAIN",
"MODELS",
"OLLAMA",
"OPENAI",
"RAG",
]
SRC_LOG_LEVELS = {} SRC_LOG_LEVELS = {}
...@@ -141,7 +153,7 @@ if CUSTOM_NAME: ...@@ -141,7 +153,7 @@ if CUSTOM_NAME:
data = r.json() data = r.json()
if r.ok: if r.ok:
if "logo" in data: if "logo" in data:
url = ( WEBUI_FAVICON_URL = url = (
f"https://api.openwebui.com{data['logo']}" f"https://api.openwebui.com{data['logo']}"
if data["logo"][0] == "/" if data["logo"][0] == "/"
else data["logo"] else data["logo"]
...@@ -238,7 +250,7 @@ OLLAMA_API_BASE_URL = os.environ.get( ...@@ -238,7 +250,7 @@ OLLAMA_API_BASE_URL = os.environ.get(
) )
OLLAMA_BASE_URL = os.environ.get("OLLAMA_BASE_URL", "") OLLAMA_BASE_URL = os.environ.get("OLLAMA_BASE_URL", "")
K8S_FLAG = os.environ.get("K8S_FLAG", "")
if OLLAMA_BASE_URL == "" and OLLAMA_API_BASE_URL != "": if OLLAMA_BASE_URL == "" and OLLAMA_API_BASE_URL != "":
OLLAMA_BASE_URL = ( OLLAMA_BASE_URL = (
...@@ -251,6 +263,9 @@ if ENV == "prod": ...@@ -251,6 +263,9 @@ if ENV == "prod":
if OLLAMA_BASE_URL == "/ollama": if OLLAMA_BASE_URL == "/ollama":
OLLAMA_BASE_URL = "http://host.docker.internal:11434" OLLAMA_BASE_URL = "http://host.docker.internal:11434"
elif K8S_FLAG:
OLLAMA_BASE_URL = "http://ollama-service.open-webui.svc.cluster.local:11434"
OLLAMA_BASE_URLS = os.environ.get("OLLAMA_BASE_URLS", "") OLLAMA_BASE_URLS = os.environ.get("OLLAMA_BASE_URLS", "")
OLLAMA_BASE_URLS = OLLAMA_BASE_URLS if OLLAMA_BASE_URLS != "" else OLLAMA_BASE_URL OLLAMA_BASE_URLS = OLLAMA_BASE_URLS if OLLAMA_BASE_URLS != "" else OLLAMA_BASE_URL
......
...@@ -60,3 +60,5 @@ class ERROR_MESSAGES(str, Enum): ...@@ -60,3 +60,5 @@ class ERROR_MESSAGES(str, Enum):
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" OPENAI_NOT_FOUND = lambda name="": f"OpenAI API was not found"
OLLAMA_NOT_FOUND = "WebUI could not connect to Ollama" OLLAMA_NOT_FOUND = "WebUI could not connect to Ollama"
EMPTY_CONTENT = "The content provided is empty. Please ensure that there is text or data present before proceeding."
{ {
"version": 0, "version": 0,
"ui": { "ui": {
"default_locale": "en-US",
"prompt_suggestions": [ "prompt_suggestions": [
{ {
"title": [ "title": [
......
...@@ -32,6 +32,7 @@ from utils.utils import get_admin_user ...@@ -32,6 +32,7 @@ from utils.utils import get_admin_user
from apps.rag.utils import rag_messages from apps.rag.utils import rag_messages
from config import ( from config import (
CONFIG_DATA,
WEBUI_NAME, WEBUI_NAME,
ENV, ENV,
VERSION, VERSION,
...@@ -49,6 +50,7 @@ logging.basicConfig(stream=sys.stdout, level=GLOBAL_LOG_LEVEL) ...@@ -49,6 +50,7 @@ logging.basicConfig(stream=sys.stdout, level=GLOBAL_LOG_LEVEL)
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
log.setLevel(SRC_LOG_LEVELS["MAIN"]) log.setLevel(SRC_LOG_LEVELS["MAIN"])
class SPAStaticFiles(StaticFiles): class SPAStaticFiles(StaticFiles):
async def get_response(self, path: str, scope): async def get_response(self, path: str, scope):
try: try:
...@@ -88,7 +90,6 @@ class RAGMiddleware(BaseHTTPMiddleware): ...@@ -88,7 +90,6 @@ class RAGMiddleware(BaseHTTPMiddleware):
# Example: Add a new key-value pair or modify existing ones # Example: Add a new key-value pair or modify existing ones
# data["modified"] = True # Example modification # data["modified"] = True # Example modification
if "docs" in data: if "docs" in data:
data = {**data} data = {**data}
data["messages"] = rag_messages( data["messages"] = rag_messages(
data["docs"], data["docs"],
...@@ -163,11 +164,15 @@ app.mount("/rag/api/v1", rag_app) ...@@ -163,11 +164,15 @@ app.mount("/rag/api/v1", rag_app)
@app.get("/api/config") @app.get("/api/config")
async def get_app_config(): async def get_app_config():
return { return {
"status": True, "status": True,
"name": WEBUI_NAME, "name": WEBUI_NAME,
"version": VERSION, "version": VERSION,
"default_locale": (
CONFIG_DATA["ui"]["default_locale"]
if "default_locale" in CONFIG_DATA["ui"]
else "en-US"
),
"images": images_app.state.ENABLED, "images": images_app.state.ENABLED,
"default_models": webui_app.state.DEFAULT_MODELS, "default_models": webui_app.state.DEFAULT_MODELS,
"default_prompt_suggestions": webui_app.state.DEFAULT_PROMPT_SUGGESTIONS, "default_prompt_suggestions": webui_app.state.DEFAULT_PROMPT_SUGGESTIONS,
...@@ -191,7 +196,6 @@ class ModelFilterConfigForm(BaseModel): ...@@ -191,7 +196,6 @@ class ModelFilterConfigForm(BaseModel):
async def update_model_filter_config( async def update_model_filter_config(
form_data: ModelFilterConfigForm, user=Depends(get_admin_user) form_data: ModelFilterConfigForm, user=Depends(get_admin_user)
): ):
app.state.MODEL_FILTER_ENABLED = form_data.enabled app.state.MODEL_FILTER_ENABLED = form_data.enabled
app.state.MODEL_FILTER_LIST = form_data.models app.state.MODEL_FILTER_LIST = form_data.models
...@@ -201,6 +205,9 @@ async def update_model_filter_config( ...@@ -201,6 +205,9 @@ async def update_model_filter_config(
openai_app.state.MODEL_FILTER_ENABLED = app.state.MODEL_FILTER_ENABLED openai_app.state.MODEL_FILTER_ENABLED = app.state.MODEL_FILTER_ENABLED
openai_app.state.MODEL_FILTER_LIST = app.state.MODEL_FILTER_LIST openai_app.state.MODEL_FILTER_LIST = app.state.MODEL_FILTER_LIST
litellm_app.state.MODEL_FILTER_ENABLED = app.state.MODEL_FILTER_ENABLED
litellm_app.state.MODEL_FILTER_LIST = app.state.MODEL_FILTER_LIST
return { return {
"enabled": app.state.MODEL_FILTER_ENABLED, "enabled": app.state.MODEL_FILTER_ENABLED,
"models": app.state.MODEL_FILTER_LIST, "models": app.state.MODEL_FILTER_LIST,
...@@ -231,7 +238,6 @@ async def update_webhook_url(form_data: UrlForm, user=Depends(get_admin_user)): ...@@ -231,7 +238,6 @@ async def update_webhook_url(form_data: UrlForm, user=Depends(get_admin_user)):
@app.get("/api/version") @app.get("/api/version")
async def get_app_config(): async def get_app_config():
return { return {
"version": VERSION, "version": VERSION,
} }
...@@ -239,7 +245,7 @@ async def get_app_config(): ...@@ -239,7 +245,7 @@ async def get_app_config():
@app.get("/api/changelog") @app.get("/api/changelog")
async def get_app_changelog(): async def get_app_changelog():
return CHANGELOG return {key: CHANGELOG[key] for idx, key in enumerate(CHANGELOG) if idx < 5}
@app.get("/api/version/updates") @app.get("/api/version/updates")
......
import json
import requests import requests
from config import VERSION, WEBUI_FAVICON_URL, WEBUI_NAME
def post_webhook(url: str, message: str, event_data: dict) -> bool: def post_webhook(url: str, message: str, event_data: dict) -> bool:
try: try:
payload = {} payload = {}
if "https://hooks.slack.com" in url: # Slack and Google Chat Webhooks
if "https://hooks.slack.com" in url or "https://chat.googleapis.com" in url:
payload["text"] = message payload["text"] = message
# Discord Webhooks
elif "https://discord.com/api/webhooks" in url: elif "https://discord.com/api/webhooks" in url:
payload["content"] = message payload["content"] = message
# Microsoft Teams Webhooks
elif "webhook.office.com" in url:
action = event_data.get("action", "undefined")
facts = [
{"name": name, "value": value}
for name, value in json.loads(event_data.get("user", {})).items()
]
payload = {
"@type": "MessageCard",
"@context": "http://schema.org/extensions",
"themeColor": "0076D7",
"summary": message,
"sections": [
{
"activityTitle": message,
"activitySubtitle": f"{WEBUI_NAME} ({VERSION}) - {action}",
"activityImage": WEBUI_FAVICON_URL,
"facts": facts,
"markdown": True,
}
],
}
# Default Payload
else: else:
payload = {**event_data} payload = {**event_data}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment