Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Release
on:
push:
branches:
- main # or whatever branch you want to use
- master # or whatever branch you want to use

jobs:
release:
Expand Down
257 changes: 65 additions & 192 deletions README.md

Large diffs are not rendered by default.

51 changes: 37 additions & 14 deletions backend/open_webui/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -956,7 +956,7 @@ def feishu_oauth_register(oauth: OAuth):
ENABLE_OLLAMA_API = PersistentConfig(
"ENABLE_OLLAMA_API",
"ollama.enable",
os.environ.get("ENABLE_OLLAMA_API", "True").lower() == "true",
os.environ.get("ENABLE_OLLAMA_API", "False").lower() == "true",
)

OLLAMA_API_BASE_URL = os.environ.get(
Expand Down Expand Up @@ -1114,14 +1114,14 @@ def feishu_oauth_register(oauth: OAuth):
(
False
if not WEBUI_AUTH
else os.environ.get("ENABLE_SIGNUP", "True").lower() == "true"
else os.environ.get("ENABLE_SIGNUP", "False").lower() == "true"
),
)

ENABLE_LOGIN_FORM = PersistentConfig(
"ENABLE_LOGIN_FORM",
"ui.ENABLE_LOGIN_FORM",
os.environ.get("ENABLE_LOGIN_FORM", "True").lower() == "true",
os.environ.get("ENABLE_LOGIN_FORM", "False").lower() == "true",
)


Expand Down Expand Up @@ -1322,11 +1322,11 @@ def feishu_oauth_register(oauth: OAuth):
)

USER_PERMISSIONS_CHAT_TTS = (
os.environ.get("USER_PERMISSIONS_CHAT_TTS", "True").lower() == "true"
os.environ.get("USER_PERMISSIONS_CHAT_TTS", "False").lower() == "true"
)

USER_PERMISSIONS_CHAT_CALL = (
os.environ.get("USER_PERMISSIONS_CHAT_CALL", "True").lower() == "true"
os.environ.get("USER_PERMISSIONS_CHAT_CALL", "False").lower() == "true"
)

USER_PERMISSIONS_CHAT_MULTIPLE_MODELS = (
Expand Down Expand Up @@ -1426,13 +1426,13 @@ def feishu_oauth_register(oauth: OAuth):
ENABLE_NOTES = PersistentConfig(
"ENABLE_NOTES",
"notes.enable",
os.environ.get("ENABLE_NOTES", "True").lower() == "true",
os.environ.get("ENABLE_NOTES", "False").lower() == "true",
)

ENABLE_EVALUATION_ARENA_MODELS = PersistentConfig(
"ENABLE_EVALUATION_ARENA_MODELS",
"evaluation.arena.enable",
os.environ.get("ENABLE_EVALUATION_ARENA_MODELS", "True").lower() == "true",
os.environ.get("ENABLE_EVALUATION_ARENA_MODELS", "False").lower() == "true",
)
EVALUATION_ARENA_MODELS = PersistentConfig(
"EVALUATION_ARENA_MODELS",
Expand Down Expand Up @@ -1475,7 +1475,7 @@ def feishu_oauth_register(oauth: OAuth):
ENABLE_COMMUNITY_SHARING = PersistentConfig(
"ENABLE_COMMUNITY_SHARING",
"ui.enable_community_sharing",
os.environ.get("ENABLE_COMMUNITY_SHARING", "True").lower() == "true",
os.environ.get("ENABLE_COMMUNITY_SHARING", "False").lower() == "true",
)

ENABLE_MESSAGE_RATING = PersistentConfig(
Expand Down Expand Up @@ -1595,26 +1595,49 @@ class BannerModel(BaseModel):
"task.title.prompt_template",
os.environ.get("TITLE_GENERATION_PROMPT_TEMPLATE", ""),
)
# TODO: re-enable examples after testing
# DEFAULT_TITLE_GENERATION_PROMPT_TEMPLATE = """### Task:
# Generate a concise, 3-5 word title with an emoji summarizing the chat history.
# ### Guidelines:
# - The title should clearly represent the main theme or subject of the conversation.
# - Use emojis that enhance understanding of the topic, but avoid quotation marks or special formatting.
# - Write the title in the chat's primary language; default to English if multilingual.
# - Prioritize accuracy over excessive creativity; keep it clear and simple.
# - Your entire response must consist solely of the JSON object, without any introductory or concluding text.
# - The output must be a single, raw JSON object, without any markdown code fences or other encapsulating text.
# - Ensure no conversational text, affirmations, or explanations precede or follow the raw JSON output, as this will cause direct parsing failure.
# ### Output:
# JSON format: { "title": "your concise title here" }
# ### Examples:
# - { "title": "📉 Stock Market Trends" },
# - { "title": "🍪 Perfect Chocolate Chip Recipe" },
# - { "title": "Evolution of Music Streaming" },
# - { "title": "Remote Work Productivity Tips" },
# - { "title": "Artificial Intelligence in Healthcare" },
# - { "title": "🎮 Video Game Development Insights" }
# ### Chat History:
# <chat_history>
# {{MESSAGES:END:2}}
# </chat_history>"""

DEFAULT_TITLE_GENERATION_PROMPT_TEMPLATE = """### Task:
Generate a concise, 3-5 word title with an emoji summarizing the chat history.
Generate a concise, 3-5 word title.
### Guidelines:
- The title should clearly represent the main theme or subject of the conversation.
- Use emojis that enhance understanding of the topic, but avoid quotation marks or special formatting.
- Write the title in the chat's primary language; default to English if multilingual.
- Use the primary language of the chat to write the title; if the chat contains multiple languages, generate the title in the primary language used.
- Prioritize accuracy over excessive creativity; keep it clear and simple.
- Your entire response must consist solely of the JSON object, without any introductory or concluding text.
- The output must be a single, raw JSON object, without any markdown code fences or other encapsulating text.
- Ensure no conversational text, affirmations, or explanations precede or follow the raw JSON output, as this will cause direct parsing failure.
### Output:
JSON format: { "title": "your concise title here" }
### Examples:
- { "title": "📉 Stock Market Trends" },
- { "title": "🍪 Perfect Chocolate Chip Recipe" },
- { "title": "Stock Market Trends" },
- { "title": "Perfect Chocolate Chip Recipe" },
- { "title": "Evolution of Music Streaming" },
- { "title": "Remote Work Productivity Tips" },
- { "title": "Artificial Intelligence in Healthcare" },
- { "title": "🎮 Video Game Development Insights" }
- { "title": "Video Game Development Insights" }
### Chat History:
<chat_history>
{{MESSAGES:END:2}}
Expand Down
2 changes: 1 addition & 1 deletion backend/open_webui/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@

log.setLevel(SRC_LOG_LEVELS["CONFIG"])

WEBUI_NAME = os.environ.get("WEBUI_NAME", "Open WebUI")
WEBUI_NAME = os.environ.get("WEBUI_NAME", "JumpServer Chat AI &")
if WEBUI_NAME != "Open WebUI":
WEBUI_NAME += " (Open WebUI)"

Expand Down
12 changes: 6 additions & 6 deletions backend/open_webui/jms/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,25 @@ class ChatHandler(BaseWisp):
def list(self, query: Optional[Dict[str, Any]] = None) -> List[dict]:
query = self._normalize_query(query)
resp = self._request("GET", CHAT_URL, query=query, action="list chats")
return self._loads(resp.body)
return self._loads(CHAT_URL, resp.body)

def retrieve(self, chat_id: str) -> dict:
self._ensure_id(chat_id)
path = f"{CHAT_URL}{chat_id}/"
resp = self._request("GET", path, action=f"retrieve chat {chat_id}")
return self._loads(resp.body)
return self._loads(path, resp.body)

def create(self, data: Dict[str, Any]) -> dict:
body = self._dumps(data)
resp = self._request("POST", CHAT_URL, body=body, action="create chat")
return self._loads(resp.body)
return self._loads(CHAT_URL, resp.body)

def update(self, chat_id: Optional[str] = None, data: Dict[str, Any] = None, query: Dict[str, Any] = None) -> dict:
self._ensure_id(chat_id)
path = f"{CHAT_URL}{chat_id}/"
body = self._dumps(data)
resp = self._request("PATCH", path, query=query, body=body, action=f"update chat {chat_id}")
return self._loads(resp.body)
return self._loads(path, resp.body)

def destroy(self, chat_id: Optional[str] = None, query: Dict[str, Any] = None) -> None:
"""
Expand Down Expand Up @@ -100,11 +100,11 @@ def _dumps(data: Dict[str, Any]) -> bytes:
raise WispError(f"Failed to serialize request body: {e}") from e

@staticmethod
def _loads(b: bytes) -> Any:
def _loads(path: str, b: bytes) -> Any:
try:
return json.loads(b.decode("utf-8")) if b else None
except (UnicodeDecodeError, json.JSONDecodeError) as e:
raise WispError(f"Failed to parse response body: {e}") from e
raise WispError(f"Failed to parse response body: {e} path {path}") from e

@staticmethod
def _safe_decode(b: Optional[bytes]) -> str:
Expand Down
43 changes: 22 additions & 21 deletions backend/open_webui/models/chats.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ class ChatResponse(BaseModel):
archived: bool
pinned: Optional[bool] = False
meta: dict = {}
session_info: dict = {}
folder_id: Optional[str] = None


Expand Down Expand Up @@ -150,7 +151,7 @@ def insert_new_chat(self, form_data: ChatForm, sid: str, request: Request, user:
'title': form_data.chat['title'] if 'title' in form_data.chat else 'New Chat',
'chat': form_data.chat,
'socket_id': sid,
'folder_id': form_data.folder_id,
'folder_id': form_data.folder_id or '',
'session_info': {
'org_id': session.org_id,
'asset': session.asset,
Expand Down Expand Up @@ -194,7 +195,7 @@ def import_chat(
}
)

result = Chat(**chat.model_dump())
result = Chat(**chat)
db.add(result)
db.commit()
db.refresh(result)
Expand Down Expand Up @@ -230,7 +231,7 @@ def update_chat_tags_by_id(

self.delete_all_tags_by_id_and_user_id(id, user.id)

for tag in chat.meta.get("tags", []):
for tag in chat['meta'].get("tags", []):
if self.count_chats_by_tag_name_and_user_id(tag, user.id) == 0:
Tags.delete_tag_by_name_and_user_id(tag, user.id)

Expand Down Expand Up @@ -315,19 +316,19 @@ def insert_shared_chat_by_chat_id(self, chat_id: str) -> Optional[ChatModel]:
# Get the existing chat to share
chat = db.get(Chat, chat_id)
# Check if the chat is already shared
if chat.share_id:
return self.get_chat_by_id_and_user_id(chat.share_id, "shared")
if chat['share_id']:
return self.get_chat_by_id_and_user_id(chat['share_id'], "shared")
# Create a new chat with the same data, but with a new ID
shared_chat = ChatModel(
**{
"id": str(uuid.uuid4()),
"user_id": f"shared-{chat_id}",
"title": chat.title,
"chat": chat.chat,
"meta": chat.meta,
"pinned": chat.pinned,
"folder_id": chat.folder_id,
"created_at": chat.created_at,
"title": chat['title'],
"chat": chat['chat'],
"meta": chat['meta'],
"pinned": chat['pinned'],
"folder_id": chat['folder_id'],
"created_at": chat['created_at'],
"updated_at": int(time.time()),
}
)
Expand Down Expand Up @@ -357,11 +358,11 @@ def update_shared_chat_by_chat_id(self, chat_id: str) -> Optional[ChatModel]:
if shared_chat is None:
return self.insert_shared_chat_by_chat_id(chat_id)

shared_chat.title = chat.title
shared_chat.chat = chat.chat
shared_chat.meta = chat.meta
shared_chat.pinned = chat.pinned
shared_chat.folder_id = chat.folder_id
shared_chat.title = chat['title']
shared_chat.chat = chat['chat']
shared_chat.meta = chat['meta']
shared_chat.pinned = chat['pinned']
shared_chat.folder_id = chat['folder_id']
shared_chat.updated_at = int(time.time())
db.commit()
db.refresh(shared_chat)
Expand Down Expand Up @@ -702,7 +703,7 @@ def update_chat_folder_id_by_id_and_user_id(
def get_chat_tags_by_id_and_user_id(self, id: str, user_id: str) -> list[TagModel]:
with get_db() as db:
chat = db.get(Chat, id)
tags = chat.meta.get("tags", [])
tags = chat['meta'].get("tags", [])
return [Tags.get_tag_by_name_and_user_id(tag, user_id) for tag in tags]

# TODO
Expand Down Expand Up @@ -749,10 +750,10 @@ def add_chat_tag_by_id_and_user_id_and_tag_name(
chat = db.get(Chat, _id)

tag_id = tag.id
if tag_id not in chat.meta.get("tags", []):
if tag_id not in chat['meta'].get("tags", []):
chat.meta = {
**chat.meta,
"tags": list(set(chat.meta.get("tags", []) + [tag_id])),
**chat['meta'],
"tags": list(set(chat['meta'].get("tags", []) + [tag_id])),
}

db.commit()
Expand Down Expand Up @@ -882,7 +883,7 @@ def delete_shared_chats_by_user_id(self, user_id: str) -> bool:
try:
with get_db() as db:
chats_by_user = db.query(Chat).filter_by(user_id=user_id).all()
shared_chat_ids = [f"shared-{chat.id}" for chat in chats_by_user]
shared_chat_ids = [f"shared-{chat['id']}" for chat in chats_by_user]

db.query(Chat).filter(Chat.user_id.in_(shared_chat_ids)).delete()
db.commit()
Expand Down
4 changes: 2 additions & 2 deletions backend/open_webui/retrieval/models/external.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ def predict(
{
"X-OpenWebUI-User-Name": quote(user.name, safe=" "),
"X-OpenWebUI-User-Id": user.id,
"X-OpenWebUI-User-Email": user.email,
"X-OpenWebUI-User-Role": user.role,
"X-OpenWebUI-User-Username": user.username,
"X-OpenWebUI-User-Role": 'admin',
}
if ENABLE_FORWARD_USER_INFO_HEADERS and user
else {}
Expand Down
21 changes: 11 additions & 10 deletions backend/open_webui/retrieval/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -595,9 +595,9 @@ def get_sources_from_items(
# Chat Attached
chat = Chats.get_chat_by_id(item.get("id"))

if chat and (user.role == "admin" or chat.user_id == user.id):
messages_map = chat.chat.get("history", {}).get("messages", {})
message_id = chat.chat.get("history", {}).get("currentId")
if chat and (user.role == "admin" or chat['user_id'] == user.id):
messages_map = chat['chat'].get("history", {}).get("messages", {})
message_id = chat['chat'].get("history", {}).get("currentId")

if messages_map and message_id:
# Reconstruct the message list in order
Expand All @@ -612,7 +612,7 @@ def get_sources_from_items(
# User has access to the chat
query_result = {
"documents": [[message_history]],
"metadatas": [[{"file_id": chat.id, "name": chat.title}]],
"metadatas": [[{"file_id": chat['id'], "name": chat['title']}]],
}

elif item.get("type") == "url":
Expand Down Expand Up @@ -797,6 +797,7 @@ def get_sources_from_items(

def get_model_path(model: str, update_model: bool = False):
# Construct huggingface_hub kwargs with local_files_only to return the snapshot path
return
cache_dir = os.getenv("SENTENCE_TRANSFORMERS_HOME")

local_files_only = not update_model
Expand Down Expand Up @@ -861,8 +862,8 @@ def generate_openai_batch_embeddings(
{
"X-OpenWebUI-User-Name": quote(user.name, safe=" "),
"X-OpenWebUI-User-Id": user.id,
"X-OpenWebUI-User-Email": user.email,
"X-OpenWebUI-User-Role": user.role,
"X-OpenWebUI-User-Username": user.username,
"X-OpenWebUI-User-Role": 'admin',
}
if ENABLE_FORWARD_USER_INFO_HEADERS and user
else {}
Expand Down Expand Up @@ -910,8 +911,8 @@ def generate_azure_openai_batch_embeddings(
{
"X-OpenWebUI-User-Name": quote(user.name, safe=" "),
"X-OpenWebUI-User-Id": user.id,
"X-OpenWebUI-User-Email": user.email,
"X-OpenWebUI-User-Role": user.role,
"X-OpenWebUI-User-Username": user.username,
"X-OpenWebUI-User-Role": 'admin',
}
if ENABLE_FORWARD_USER_INFO_HEADERS and user
else {}
Expand Down Expand Up @@ -960,8 +961,8 @@ def generate_ollama_batch_embeddings(
{
"X-OpenWebUI-User-Name": quote(user.name, safe=" "),
"X-OpenWebUI-User-Id": user.id,
"X-OpenWebUI-User-Email": user.email,
"X-OpenWebUI-User-Role": user.role,
"X-OpenWebUI-User-Username": user.username,
"X-OpenWebUI-User-Role": 'admin',
}
if ENABLE_FORWARD_USER_INFO_HEADERS
else {}
Expand Down
4 changes: 2 additions & 2 deletions backend/open_webui/routers/audio.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,8 +374,8 @@ async def speech(request: Request, user=Depends(get_verified_user)):
{
"X-OpenWebUI-User-Name": quote(user.name, safe=" "),
"X-OpenWebUI-User-Id": user.id,
"X-OpenWebUI-User-Email": user.email,
"X-OpenWebUI-User-Role": user.role,
"X-OpenWebUI-User-Username": user.username,
"X-OpenWebUI-User-Role": 'admin',
}
if ENABLE_FORWARD_USER_INFO_HEADERS
else {}
Expand Down
Loading
Loading