2023-12-26 09:27:43 +00:00
|
|
|
|
import json
|
2024-03-20 23:11:36 +00:00
|
|
|
|
import logging
|
2024-08-27 22:10:27 +00:00
|
|
|
|
from typing import Optional
|
2023-12-26 05:44:28 +00:00
|
|
|
|
|
2025-03-26 08:10:27 +00:00
|
|
|
|
|
|
|
|
|
|
from open_webui.socket.main import get_event_emitter
|
2024-12-10 08:54:13 +00:00
|
|
|
|
from open_webui.models.chats import (
|
2024-09-04 14:54:48 +00:00
|
|
|
|
ChatForm,
|
2024-10-18 03:13:28 +00:00
|
|
|
|
ChatImportForm,
|
2024-09-04 14:54:48 +00:00
|
|
|
|
ChatResponse,
|
|
|
|
|
|
Chats,
|
|
|
|
|
|
ChatTitleIdResponse,
|
|
|
|
|
|
)
|
2024-12-10 08:54:13 +00:00
|
|
|
|
from open_webui.models.tags import TagModel, Tags
|
|
|
|
|
|
from open_webui.models.folders import Folders
|
2024-10-11 06:22:53 +00:00
|
|
|
|
|
2024-09-04 14:54:48 +00:00
|
|
|
|
from open_webui.config import ENABLE_ADMIN_CHAT_ACCESS, ENABLE_ADMIN_EXPORT
|
|
|
|
|
|
from open_webui.constants import ERROR_MESSAGES
|
|
|
|
|
|
from open_webui.env import SRC_LOG_LEVELS
|
2024-08-27 22:10:27 +00:00
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
|
|
|
|
|
from pydantic import BaseModel
|
2024-11-17 05:31:57 +00:00
|
|
|
|
|
|
|
|
|
|
|
2024-12-09 00:01:56 +00:00
|
|
|
|
from open_webui.utils.auth import get_admin_user, get_verified_user
|
2024-11-17 05:31:57 +00:00
|
|
|
|
from open_webui.utils.access_control import has_permission
|
2024-03-31 08:13:39 +00:00
|
|
|
|
|
2024-03-20 23:11:36 +00:00
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
log.setLevel(SRC_LOG_LEVELS["MODELS"])
|
|
|
|
|
|
|
2023-12-26 05:44:28 +00:00
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
|
|
|
|
|
|
############################
|
2024-04-27 22:12:57 +00:00
|
|
|
|
# GetChatList
|
2023-12-26 05:44:28 +00:00
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-08-14 12:46:31 +00:00
|
|
|
|
@router.get("/", response_model=list[ChatTitleIdResponse])
|
|
|
|
|
|
@router.get("/list", response_model=list[ChatTitleIdResponse])
|
2025-08-14 12:56:08 +00:00
|
|
|
|
def get_session_user_chat_list(
|
2025-09-24 16:27:19 +00:00
|
|
|
|
user=Depends(get_verified_user),
|
|
|
|
|
|
page: Optional[int] = None,
|
2025-10-14 23:06:29 +00:00
|
|
|
|
include_pinned: Optional[bool] = False,
|
2025-09-24 16:27:19 +00:00
|
|
|
|
include_folders: Optional[bool] = False,
|
2024-01-17 22:47:56 +00:00
|
|
|
|
):
|
2025-07-16 10:54:56 +00:00
|
|
|
|
try:
|
|
|
|
|
|
if page is not None:
|
|
|
|
|
|
limit = 60
|
|
|
|
|
|
skip = (page - 1) * limit
|
|
|
|
|
|
|
|
|
|
|
|
return Chats.get_chat_title_id_list_by_user_id(
|
2025-10-14 23:06:29 +00:00
|
|
|
|
user.id,
|
|
|
|
|
|
include_folders=include_folders,
|
|
|
|
|
|
include_pinned=include_pinned,
|
|
|
|
|
|
skip=skip,
|
|
|
|
|
|
limit=limit,
|
2025-07-16 10:54:56 +00:00
|
|
|
|
)
|
|
|
|
|
|
else:
|
2025-09-24 16:27:19 +00:00
|
|
|
|
return Chats.get_chat_title_id_list_by_user_id(
|
2025-10-14 23:06:29 +00:00
|
|
|
|
user.id, include_folders=include_folders, include_pinned=include_pinned
|
2025-09-24 16:27:19 +00:00
|
|
|
|
)
|
2025-07-16 10:54:56 +00:00
|
|
|
|
except Exception as e:
|
|
|
|
|
|
log.exception(e)
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
|
|
|
|
|
|
)
|
2024-04-27 22:12:57 +00:00
|
|
|
|
|
|
|
|
|
|
|
2024-04-27 22:14:15 +00:00
|
|
|
|
############################
|
|
|
|
|
|
# DeleteAllChats
|
|
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.delete("/", response_model=bool)
|
2024-06-27 18:29:59 +00:00
|
|
|
|
async def delete_all_user_chats(request: Request, user=Depends(get_verified_user)):
|
2024-11-17 05:31:57 +00:00
|
|
|
|
|
|
|
|
|
|
if user.role == "user" and not has_permission(
|
|
|
|
|
|
user.id, "chat.delete", request.app.state.config.USER_PERMISSIONS
|
|
|
|
|
|
):
|
2024-04-27 22:14:15 +00:00
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
|
|
|
|
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2024-06-21 12:58:57 +00:00
|
|
|
|
result = Chats.delete_chats_by_user_id(user.id)
|
2024-04-27 22:14:15 +00:00
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-04-27 22:12:57 +00:00
|
|
|
|
############################
|
|
|
|
|
|
# GetUserChatList
|
|
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-08-14 12:46:31 +00:00
|
|
|
|
@router.get("/list/user/{user_id}", response_model=list[ChatTitleIdResponse])
|
2024-04-27 22:12:57 +00:00
|
|
|
|
async def get_user_chat_list_by_user_id(
|
2024-06-18 13:03:31 +00:00
|
|
|
|
user_id: str,
|
2025-05-24 21:44:53 +00:00
|
|
|
|
page: Optional[int] = None,
|
|
|
|
|
|
query: Optional[str] = None,
|
|
|
|
|
|
order_by: Optional[str] = None,
|
|
|
|
|
|
direction: Optional[str] = None,
|
2024-06-18 13:03:31 +00:00
|
|
|
|
user=Depends(get_admin_user),
|
2024-04-27 22:12:57 +00:00
|
|
|
|
):
|
2024-08-04 14:16:14 +00:00
|
|
|
|
if not ENABLE_ADMIN_CHAT_ACCESS:
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
|
|
|
|
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
|
|
|
|
|
)
|
2025-05-24 21:44:53 +00:00
|
|
|
|
|
|
|
|
|
|
if page is None:
|
|
|
|
|
|
page = 1
|
|
|
|
|
|
|
|
|
|
|
|
limit = 60
|
|
|
|
|
|
skip = (page - 1) * limit
|
|
|
|
|
|
|
|
|
|
|
|
filter = {}
|
|
|
|
|
|
if query:
|
|
|
|
|
|
filter["query"] = query
|
|
|
|
|
|
if order_by:
|
|
|
|
|
|
filter["order_by"] = order_by
|
|
|
|
|
|
if direction:
|
|
|
|
|
|
filter["direction"] = direction
|
|
|
|
|
|
|
2024-05-26 09:00:31 +00:00
|
|
|
|
return Chats.get_chat_list_by_user_id(
|
2025-05-24 21:44:53 +00:00
|
|
|
|
user_id, include_archived=True, filter=filter, skip=skip, limit=limit
|
2024-05-26 09:00:31 +00:00
|
|
|
|
)
|
2023-12-26 05:44:28 +00:00
|
|
|
|
|
|
|
|
|
|
|
2024-04-20 23:24:18 +00:00
|
|
|
|
############################
|
2024-05-26 09:00:31 +00:00
|
|
|
|
# CreateNewChat
|
2024-05-03 22:34:08 +00:00
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-05-26 09:00:31 +00:00
|
|
|
|
@router.post("/new", response_model=Optional[ChatResponse])
|
2024-06-27 18:29:59 +00:00
|
|
|
|
async def create_new_chat(form_data: ChatForm, user=Depends(get_verified_user)):
|
2024-05-26 09:00:31 +00:00
|
|
|
|
try:
|
2024-06-21 12:58:57 +00:00
|
|
|
|
chat = Chats.insert_new_chat(user.id, form_data)
|
2024-10-09 05:02:48 +00:00
|
|
|
|
return ChatResponse(**chat.model_dump())
|
2024-05-26 09:00:31 +00:00
|
|
|
|
except Exception as e:
|
|
|
|
|
|
log.exception(e)
|
2024-05-03 22:34:08 +00:00
|
|
|
|
raise HTTPException(
|
2024-05-26 09:00:31 +00:00
|
|
|
|
status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
|
2024-05-03 22:34:08 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-10-18 03:13:28 +00:00
|
|
|
|
############################
|
|
|
|
|
|
# ImportChat
|
|
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/import", response_model=Optional[ChatResponse])
|
|
|
|
|
|
async def import_chat(form_data: ChatImportForm, user=Depends(get_verified_user)):
|
|
|
|
|
|
try:
|
|
|
|
|
|
chat = Chats.import_chat(user.id, form_data)
|
2024-10-21 01:02:41 +00:00
|
|
|
|
if chat:
|
|
|
|
|
|
tags = chat.meta.get("tags", [])
|
|
|
|
|
|
for tag_id in tags:
|
|
|
|
|
|
tag_id = tag_id.replace(" ", "_").lower()
|
2024-10-21 01:04:46 +00:00
|
|
|
|
tag_name = " ".join([word.capitalize() for word in tag_id.split("_")])
|
2024-10-21 01:02:41 +00:00
|
|
|
|
if (
|
|
|
|
|
|
tag_id != "none"
|
2024-10-21 01:04:46 +00:00
|
|
|
|
and Tags.get_tag_by_name_and_user_id(tag_name, user.id) is None
|
2024-10-21 01:02:41 +00:00
|
|
|
|
):
|
2024-10-21 01:04:46 +00:00
|
|
|
|
Tags.insert_new_tag(tag_name, user.id)
|
2024-10-21 01:02:41 +00:00
|
|
|
|
|
2024-10-18 03:13:28 +00:00
|
|
|
|
return ChatResponse(**chat.model_dump())
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
log.exception(e)
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-12-27 06:10:22 +00:00
|
|
|
|
############################
|
2024-04-27 22:12:57 +00:00
|
|
|
|
# GetChats
|
2023-12-27 06:10:22 +00:00
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-10-09 06:37:37 +00:00
|
|
|
|
@router.get("/search", response_model=list[ChatTitleIdResponse])
|
2025-09-16 15:40:15 +00:00
|
|
|
|
def search_user_chats(
|
2024-10-09 06:37:37 +00:00
|
|
|
|
text: str, page: Optional[int] = None, user=Depends(get_verified_user)
|
|
|
|
|
|
):
|
|
|
|
|
|
if page is None:
|
|
|
|
|
|
page = 1
|
|
|
|
|
|
|
|
|
|
|
|
limit = 60
|
|
|
|
|
|
skip = (page - 1) * limit
|
|
|
|
|
|
|
2024-10-15 05:57:11 +00:00
|
|
|
|
chat_list = [
|
2024-10-09 06:37:37 +00:00
|
|
|
|
ChatTitleIdResponse(**chat.model_dump())
|
|
|
|
|
|
for chat in Chats.get_chats_by_user_id_and_search_text(
|
|
|
|
|
|
user.id, text, skip=skip, limit=limit
|
|
|
|
|
|
)
|
|
|
|
|
|
]
|
|
|
|
|
|
|
2024-10-15 05:57:11 +00:00
|
|
|
|
# Delete tag if no chat is found
|
|
|
|
|
|
words = text.strip().split(" ")
|
|
|
|
|
|
if page == 1 and len(words) == 1 and words[0].startswith("tag:"):
|
|
|
|
|
|
tag_id = words[0].replace("tag:", "")
|
|
|
|
|
|
if len(chat_list) == 0:
|
|
|
|
|
|
if Tags.get_tag_by_name_and_user_id(tag_id, user.id):
|
|
|
|
|
|
log.debug(f"deleting tag: {tag_id}")
|
|
|
|
|
|
Tags.delete_tag_by_name_and_user_id(tag_id, user.id)
|
|
|
|
|
|
|
|
|
|
|
|
return chat_list
|
|
|
|
|
|
|
2024-10-09 06:37:37 +00:00
|
|
|
|
|
2024-10-19 09:42:12 +00:00
|
|
|
|
############################
|
|
|
|
|
|
# GetChatsByFolderId
|
|
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/folder/{folder_id}", response_model=list[ChatResponse])
|
|
|
|
|
|
async def get_chats_by_folder_id(folder_id: str, user=Depends(get_verified_user)):
|
|
|
|
|
|
folder_ids = [folder_id]
|
|
|
|
|
|
children_folders = Folders.get_children_folders_by_id_and_user_id(
|
|
|
|
|
|
folder_id, user.id
|
|
|
|
|
|
)
|
|
|
|
|
|
if children_folders:
|
|
|
|
|
|
folder_ids.extend([folder.id for folder in children_folders])
|
|
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
|
ChatResponse(**chat.model_dump())
|
|
|
|
|
|
for chat in Chats.get_chats_by_folder_ids_and_user_id(folder_ids, user.id)
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-09-27 01:48:17 +00:00
|
|
|
|
@router.get("/folder/{folder_id}/list")
|
|
|
|
|
|
async def get_chat_list_by_folder_id(
|
|
|
|
|
|
folder_id: str, page: Optional[int] = 1, user=Depends(get_verified_user)
|
|
|
|
|
|
):
|
|
|
|
|
|
try:
|
|
|
|
|
|
limit = 60
|
|
|
|
|
|
skip = (page - 1) * limit
|
|
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
|
{"title": chat.title, "id": chat.id, "updated_at": chat.updated_at}
|
|
|
|
|
|
for chat in Chats.get_chats_by_folder_id_and_user_id(
|
|
|
|
|
|
folder_id, user.id, skip=skip, limit=limit
|
|
|
|
|
|
)
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
log.exception(e)
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-10-11 06:22:53 +00:00
|
|
|
|
############################
|
|
|
|
|
|
# GetPinnedChats
|
|
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-05-26 18:15:21 +00:00
|
|
|
|
@router.get("/pinned", response_model=list[ChatTitleIdResponse])
|
2024-10-11 06:22:53 +00:00
|
|
|
|
async def get_user_pinned_chats(user=Depends(get_verified_user)):
|
|
|
|
|
|
return [
|
2025-05-26 18:15:21 +00:00
|
|
|
|
ChatTitleIdResponse(**chat.model_dump())
|
2024-10-11 06:22:53 +00:00
|
|
|
|
for chat in Chats.get_pinned_chats_by_user_id(user.id)
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-10-09 06:37:37 +00:00
|
|
|
|
############################
|
|
|
|
|
|
# GetChats
|
|
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-08-14 12:46:31 +00:00
|
|
|
|
@router.get("/all", response_model=list[ChatResponse])
|
2024-06-27 18:29:59 +00:00
|
|
|
|
async def get_user_chats(user=Depends(get_verified_user)):
|
2023-12-28 20:15:54 +00:00
|
|
|
|
return [
|
2024-10-09 05:02:48 +00:00
|
|
|
|
ChatResponse(**chat.model_dump())
|
2024-06-21 12:58:57 +00:00
|
|
|
|
for chat in Chats.get_chats_by_user_id(user.id)
|
2024-06-03 04:39:09 +00:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
############################
|
|
|
|
|
|
# GetArchivedChats
|
|
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-08-14 12:46:31 +00:00
|
|
|
|
@router.get("/all/archived", response_model=list[ChatResponse])
|
2024-06-28 07:19:56 +00:00
|
|
|
|
async def get_user_archived_chats(user=Depends(get_verified_user)):
|
2024-06-03 04:39:09 +00:00
|
|
|
|
return [
|
2024-10-09 05:02:48 +00:00
|
|
|
|
ChatResponse(**chat.model_dump())
|
2024-06-21 12:58:57 +00:00
|
|
|
|
for chat in Chats.get_archived_chats_by_user_id(user.id)
|
2023-12-30 10:53:33 +00:00
|
|
|
|
]
|
2023-12-27 06:10:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
2024-10-11 06:22:53 +00:00
|
|
|
|
############################
|
|
|
|
|
|
# GetAllTags
|
|
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/all/tags", response_model=list[TagModel])
|
|
|
|
|
|
async def get_all_user_tags(user=Depends(get_verified_user)):
|
|
|
|
|
|
try:
|
|
|
|
|
|
tags = Tags.get_tags_by_user_id(user.id)
|
|
|
|
|
|
return tags
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
log.exception(e)
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-02-04 09:07:18 +00:00
|
|
|
|
############################
|
|
|
|
|
|
# GetAllChatsInDB
|
|
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-08-14 12:46:31 +00:00
|
|
|
|
@router.get("/all/db", response_model=list[ChatResponse])
|
2024-06-21 12:58:57 +00:00
|
|
|
|
async def get_all_user_chats_in_db(user=Depends(get_admin_user)):
|
2024-04-22 18:55:46 +00:00
|
|
|
|
if not ENABLE_ADMIN_EXPORT:
|
2024-04-17 08:33:22 +00:00
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
|
|
|
|
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
|
|
|
|
|
)
|
2024-10-09 05:02:48 +00:00
|
|
|
|
return [ChatResponse(**chat.model_dump()) for chat in Chats.get_chats()]
|
2024-02-04 09:07:18 +00:00
|
|
|
|
|
|
|
|
|
|
|
2023-12-26 05:44:28 +00:00
|
|
|
|
############################
|
2024-05-26 09:00:31 +00:00
|
|
|
|
# GetArchivedChats
|
2023-12-26 05:44:28 +00:00
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-08-14 12:46:31 +00:00
|
|
|
|
@router.get("/archived", response_model=list[ChatTitleIdResponse])
|
2024-05-26 09:00:31 +00:00
|
|
|
|
async def get_archived_session_user_chat_list(
|
2025-05-24 20:48:30 +00:00
|
|
|
|
page: Optional[int] = None,
|
|
|
|
|
|
query: Optional[str] = None,
|
2025-05-24 21:23:12 +00:00
|
|
|
|
order_by: Optional[str] = None,
|
|
|
|
|
|
direction: Optional[str] = None,
|
2025-05-24 20:48:30 +00:00
|
|
|
|
user=Depends(get_verified_user),
|
2024-05-26 09:00:31 +00:00
|
|
|
|
):
|
2025-05-24 20:48:30 +00:00
|
|
|
|
if page is None:
|
|
|
|
|
|
page = 1
|
|
|
|
|
|
|
|
|
|
|
|
limit = 60
|
|
|
|
|
|
skip = (page - 1) * limit
|
|
|
|
|
|
|
2025-05-24 21:23:12 +00:00
|
|
|
|
filter = {}
|
|
|
|
|
|
if query:
|
|
|
|
|
|
filter["query"] = query
|
|
|
|
|
|
if order_by:
|
|
|
|
|
|
filter["order_by"] = order_by
|
|
|
|
|
|
if direction:
|
|
|
|
|
|
filter["direction"] = direction
|
|
|
|
|
|
|
2025-05-24 20:48:30 +00:00
|
|
|
|
chat_list = [
|
|
|
|
|
|
ChatTitleIdResponse(**chat.model_dump())
|
|
|
|
|
|
for chat in Chats.get_archived_chat_list_by_user_id(
|
|
|
|
|
|
user.id,
|
2025-05-24 21:23:12 +00:00
|
|
|
|
filter=filter,
|
2025-05-24 20:48:30 +00:00
|
|
|
|
skip=skip,
|
|
|
|
|
|
limit=limit,
|
|
|
|
|
|
)
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
return chat_list
|
2024-05-26 09:00:31 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
############################
|
|
|
|
|
|
# ArchiveAllChats
|
|
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-06-11 08:18:05 +00:00
|
|
|
|
@router.post("/archive/all", response_model=bool)
|
2024-06-27 18:29:59 +00:00
|
|
|
|
async def archive_all_chats(user=Depends(get_verified_user)):
|
2024-06-21 12:58:57 +00:00
|
|
|
|
return Chats.archive_all_chats_by_user_id(user.id)
|
2025-09-28 17:25:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
############################
|
|
|
|
|
|
# UnarchiveAllChats
|
|
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/unarchive/all", response_model=bool)
|
|
|
|
|
|
async def unarchive_all_chats(user=Depends(get_verified_user)):
|
|
|
|
|
|
return Chats.unarchive_all_chats_by_user_id(user.id)
|
2024-05-26 09:00:31 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
############################
|
|
|
|
|
|
# GetSharedChatById
|
|
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/share/{share_id}", response_model=Optional[ChatResponse])
|
2024-06-27 18:29:59 +00:00
|
|
|
|
async def get_shared_chat_by_id(share_id: str, user=Depends(get_verified_user)):
|
2024-05-26 09:00:31 +00:00
|
|
|
|
if user.role == "pending":
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2024-08-04 15:00:52 +00:00
|
|
|
|
if user.role == "user" or (user.role == "admin" and not ENABLE_ADMIN_CHAT_ACCESS):
|
2024-06-21 12:58:57 +00:00
|
|
|
|
chat = Chats.get_chat_by_share_id(share_id)
|
2024-08-04 14:16:14 +00:00
|
|
|
|
elif user.role == "admin" and ENABLE_ADMIN_CHAT_ACCESS:
|
2024-06-21 12:58:57 +00:00
|
|
|
|
chat = Chats.get_chat_by_id(share_id)
|
2024-05-26 09:00:31 +00:00
|
|
|
|
|
|
|
|
|
|
if chat:
|
2024-10-09 05:02:48 +00:00
|
|
|
|
return ChatResponse(**chat.model_dump())
|
|
|
|
|
|
|
2024-05-26 09:00:31 +00:00
|
|
|
|
else:
|
2024-01-17 22:47:56 +00:00
|
|
|
|
raise HTTPException(
|
2024-05-26 09:00:31 +00:00
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND
|
2024-01-17 22:47:56 +00:00
|
|
|
|
)
|
2023-12-26 05:44:28 +00:00
|
|
|
|
|
|
|
|
|
|
|
2024-05-03 22:34:08 +00:00
|
|
|
|
############################
|
|
|
|
|
|
# GetChatsByTags
|
|
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-10-11 06:22:53 +00:00
|
|
|
|
class TagForm(BaseModel):
|
2024-05-03 22:34:08 +00:00
|
|
|
|
name: str
|
2024-10-11 06:22:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TagFilterForm(TagForm):
|
2024-05-03 22:34:08 +00:00
|
|
|
|
skip: Optional[int] = 0
|
|
|
|
|
|
limit: Optional[int] = 50
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-08-14 12:46:31 +00:00
|
|
|
|
@router.post("/tags", response_model=list[ChatTitleIdResponse])
|
2024-05-03 22:34:08 +00:00
|
|
|
|
async def get_user_chat_list_by_tag_name(
|
2024-10-11 06:22:53 +00:00
|
|
|
|
form_data: TagFilterForm, user=Depends(get_verified_user)
|
2024-05-03 22:34:08 +00:00
|
|
|
|
):
|
2024-10-11 06:22:53 +00:00
|
|
|
|
chats = Chats.get_chat_list_by_user_id_and_tag_name(
|
|
|
|
|
|
user.id, form_data.name, form_data.skip, form_data.limit
|
|
|
|
|
|
)
|
2024-05-03 22:34:08 +00:00
|
|
|
|
if len(chats) == 0:
|
2024-10-11 06:22:53 +00:00
|
|
|
|
Tags.delete_tag_by_name_and_user_id(form_data.name, user.id)
|
2024-05-03 22:34:08 +00:00
|
|
|
|
|
|
|
|
|
|
return chats
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-12-26 05:44:28 +00:00
|
|
|
|
############################
|
|
|
|
|
|
# GetChatById
|
|
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-12-26 09:27:43 +00:00
|
|
|
|
@router.get("/{id}", response_model=Optional[ChatResponse])
|
2024-06-27 18:29:59 +00:00
|
|
|
|
async def get_chat_by_id(id: str, user=Depends(get_verified_user)):
|
2024-06-21 12:58:57 +00:00
|
|
|
|
chat = Chats.get_chat_by_id_and_user_id(id, user.id)
|
2023-12-26 05:44:28 +00:00
|
|
|
|
|
2023-12-28 20:15:54 +00:00
|
|
|
|
if chat:
|
2024-10-09 05:02:48 +00:00
|
|
|
|
return ChatResponse(**chat.model_dump())
|
|
|
|
|
|
|
2023-12-26 05:44:28 +00:00
|
|
|
|
else:
|
2024-01-17 22:47:56 +00:00
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND
|
|
|
|
|
|
)
|
2023-12-26 05:44:28 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
############################
|
|
|
|
|
|
# UpdateChatById
|
|
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-12-26 09:27:43 +00:00
|
|
|
|
@router.post("/{id}", response_model=Optional[ChatResponse])
|
2024-01-17 22:47:56 +00:00
|
|
|
|
async def update_chat_by_id(
|
2024-06-27 18:29:59 +00:00
|
|
|
|
id: str, form_data: ChatForm, user=Depends(get_verified_user)
|
2024-01-17 22:47:56 +00:00
|
|
|
|
):
|
2025-11-27 17:52:18 +00:00
|
|
|
|
"""
|
|
|
|
|
|
更新聊天记录 - 保存聊天历史、消息、模型配置等
|
|
|
|
|
|
|
|
|
|
|
|
这是聊天数据持久化的核心接口,负责:
|
|
|
|
|
|
1. 验证用户是否有权限更新该聊天(仅聊天所有者可更新)
|
|
|
|
|
|
2. 合并现有聊天数据和前端提交的新数据
|
|
|
|
|
|
3. 更新数据库中的聊天记录
|
|
|
|
|
|
4. 返回更新后的聊天对象
|
|
|
|
|
|
|
|
|
|
|
|
前端调用场景:
|
|
|
|
|
|
- 前端接口封装:src/lib/apis/chats/index.ts:updateChatById(POST /api/chats/{id})。
|
|
|
|
|
|
- 后端路由:backend/open_webui/routers/chats.py:update_chat_by_id(处理上述请求;内部调用 Chats.update_chat_by_id)
|
|
|
|
|
|
- 模型类内部:backend/open_webui/models/chats.py 里有多处自用 update_chat_by_id(如 update_chat_folder_by_id、archive 等),但对外暴露的唯一入口仍是上面的路由
|
|
|
|
|
|
|
|
|
|
|
|
请求格式:
|
|
|
|
|
|
POST /api/chats/{id}
|
|
|
|
|
|
Body: {
|
|
|
|
|
|
"chat": {
|
|
|
|
|
|
"title": "聊天标题",
|
|
|
|
|
|
"models": ["gpt-4"],
|
|
|
|
|
|
"history": { "messages": {...}, "currentId": "..." },
|
|
|
|
|
|
"messages": [...],
|
|
|
|
|
|
"params": {...},
|
|
|
|
|
|
"files": [...],
|
|
|
|
|
|
"memory_enabled": true,
|
|
|
|
|
|
"tags": ["工作", "技术"]
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
安全策略:
|
|
|
|
|
|
- 仅允许聊天所有者更新(user.id 必须匹配 chat.user_id)
|
|
|
|
|
|
- 通过 get_chat_by_id_and_user_id 确保权限隔离
|
|
|
|
|
|
- 非所有者访问返回 401 Unauthorized
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
id: 聊天记录 ID
|
|
|
|
|
|
form_data: 聊天表单数据(ChatForm),包含 chat 字段
|
|
|
|
|
|
user: 当前登录用户(通过 JWT token 验证)
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
ChatResponse: 更新后的聊天对象,包含完整的聊天数据
|
|
|
|
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
|
|
HTTPException(401): 用户无权访问该聊天或聊天不存在
|
|
|
|
|
|
"""
|
|
|
|
|
|
# === 1. 权限验证:检查聊天是否存在且属于当前用户 ===
|
2024-06-21 12:58:57 +00:00
|
|
|
|
chat = Chats.get_chat_by_id_and_user_id(id, user.id)
|
2025-11-27 17:52:18 +00:00
|
|
|
|
|
2023-12-28 20:15:54 +00:00
|
|
|
|
if chat:
|
2025-11-27 17:52:18 +00:00
|
|
|
|
# === 2. 合并数据:将前端提交的数据合并到现有聊天数据中 ===
|
|
|
|
|
|
# 使用字典解包实现浅合并:现有数据作为基础,新数据覆盖同名字段
|
|
|
|
|
|
# 例如:现有 {"title": "旧标题", "models": ["gpt-3.5"]}
|
|
|
|
|
|
# 新数据 {"title": "新标题", "history": {...}}
|
|
|
|
|
|
# 结果:{"title": "新标题", "models": ["gpt-3.5"], "history": {...}}
|
2024-10-09 05:02:48 +00:00
|
|
|
|
updated_chat = {**chat.chat, **form_data.chat}
|
2025-11-27 17:52:18 +00:00
|
|
|
|
|
|
|
|
|
|
# === 3. 持久化:更新数据库中的聊天记录 ===
|
2024-06-21 12:58:57 +00:00
|
|
|
|
chat = Chats.update_chat_by_id(id, updated_chat)
|
2025-11-27 17:52:18 +00:00
|
|
|
|
|
|
|
|
|
|
# === 4. 返回更新后的聊天对象 ===
|
2024-10-09 05:02:48 +00:00
|
|
|
|
return ChatResponse(**chat.model_dump())
|
2023-12-28 20:15:54 +00:00
|
|
|
|
else:
|
2025-11-27 17:52:18 +00:00
|
|
|
|
# === 5. 权限拒绝:聊天不存在或不属于当前用户 ===
|
2023-12-30 10:53:33 +00:00
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
|
|
|
|
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
|
|
|
|
|
)
|
2023-12-26 09:27:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
2025-03-26 08:10:27 +00:00
|
|
|
|
############################
|
|
|
|
|
|
# UpdateChatMessageById
|
|
|
|
|
|
############################
|
|
|
|
|
|
class MessageForm(BaseModel):
|
|
|
|
|
|
content: str
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/{id}/messages/{message_id}", response_model=Optional[ChatResponse])
|
|
|
|
|
|
async def update_chat_message_by_id(
|
|
|
|
|
|
id: str, message_id: str, form_data: MessageForm, user=Depends(get_verified_user)
|
|
|
|
|
|
):
|
|
|
|
|
|
chat = Chats.get_chat_by_id(id)
|
|
|
|
|
|
|
|
|
|
|
|
if not chat:
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
|
|
|
|
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if chat.user_id != user.id and user.role != "admin":
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
|
|
|
|
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
chat = Chats.upsert_message_to_chat_by_id_and_message_id(
|
|
|
|
|
|
id,
|
|
|
|
|
|
message_id,
|
|
|
|
|
|
{
|
|
|
|
|
|
"content": form_data.content,
|
|
|
|
|
|
},
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
event_emitter = get_event_emitter(
|
|
|
|
|
|
{
|
|
|
|
|
|
"user_id": user.id,
|
|
|
|
|
|
"chat_id": id,
|
|
|
|
|
|
"message_id": message_id,
|
2025-03-27 03:28:41 +00:00
|
|
|
|
},
|
|
|
|
|
|
False,
|
2025-03-26 08:10:27 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if event_emitter:
|
2025-03-26 08:25:01 +00:00
|
|
|
|
await event_emitter(
|
2025-03-26 08:10:27 +00:00
|
|
|
|
{
|
|
|
|
|
|
"type": "chat:message",
|
|
|
|
|
|
"data": {
|
|
|
|
|
|
"chat_id": id,
|
|
|
|
|
|
"message_id": message_id,
|
|
|
|
|
|
"content": form_data.content,
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
return ChatResponse(**chat.model_dump())
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-03-27 03:28:41 +00:00
|
|
|
|
############################
|
|
|
|
|
|
# SendChatMessageEventById
|
|
|
|
|
|
############################
|
|
|
|
|
|
class EventForm(BaseModel):
|
|
|
|
|
|
type: str
|
|
|
|
|
|
data: dict
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/{id}/messages/{message_id}/event", response_model=Optional[bool])
|
|
|
|
|
|
async def send_chat_message_event_by_id(
|
|
|
|
|
|
id: str, message_id: str, form_data: EventForm, user=Depends(get_verified_user)
|
|
|
|
|
|
):
|
|
|
|
|
|
chat = Chats.get_chat_by_id(id)
|
|
|
|
|
|
|
|
|
|
|
|
if not chat:
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
|
|
|
|
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if chat.user_id != user.id and user.role != "admin":
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
|
|
|
|
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
event_emitter = get_event_emitter(
|
|
|
|
|
|
{
|
|
|
|
|
|
"user_id": user.id,
|
|
|
|
|
|
"chat_id": id,
|
|
|
|
|
|
"message_id": message_id,
|
|
|
|
|
|
}
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
if event_emitter:
|
|
|
|
|
|
await event_emitter(form_data.model_dump())
|
|
|
|
|
|
else:
|
|
|
|
|
|
return False
|
|
|
|
|
|
return True
|
|
|
|
|
|
except:
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-12-26 09:27:43 +00:00
|
|
|
|
############################
|
|
|
|
|
|
# DeleteChatById
|
|
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-11-27 18:42:00 +00:00
|
|
|
|
|
2023-12-26 09:27:43 +00:00
|
|
|
|
@router.delete("/{id}", response_model=bool)
|
2024-06-27 18:29:59 +00:00
|
|
|
|
async def delete_chat_by_id(request: Request, id: str, user=Depends(get_verified_user)):
|
2025-11-27 18:42:00 +00:00
|
|
|
|
"""
|
|
|
|
|
|
删除聊天记录 - 支持管理员和用户删除,自动清理关联资源
|
|
|
|
|
|
|
|
|
|
|
|
功能:
|
|
|
|
|
|
1. 权限验证(管理员可删除任意聊天,普通用户仅限自己的)
|
|
|
|
|
|
2. 清理 Mem0 记忆条目(删除该聊天窗口的所有记忆)
|
|
|
|
|
|
3. 清理孤立标签(仅被该聊天使用的标签)
|
|
|
|
|
|
4. 删除聊天记录
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
request: FastAPI 请求对象
|
|
|
|
|
|
id: 聊天记录 ID
|
|
|
|
|
|
user: 当前用户
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
bool: 删除成功返回 True
|
|
|
|
|
|
"""
|
|
|
|
|
|
# === 管理员分支 ===
|
2024-04-27 22:24:59 +00:00
|
|
|
|
if user.role == "admin":
|
2024-10-15 05:57:11 +00:00
|
|
|
|
chat = Chats.get_chat_by_id(id)
|
2025-11-27 18:42:00 +00:00
|
|
|
|
|
|
|
|
|
|
# 清理孤立标签(仅被该聊天使用的标签)
|
2024-10-15 05:57:11 +00:00
|
|
|
|
for tag in chat.meta.get("tags", []):
|
2024-10-15 05:59:17 +00:00
|
|
|
|
if Chats.count_chats_by_tag_name_and_user_id(tag, user.id) == 1:
|
2024-10-15 05:57:11 +00:00
|
|
|
|
Tags.delete_tag_by_name_and_user_id(tag, user.id)
|
|
|
|
|
|
|
2024-06-21 12:58:57 +00:00
|
|
|
|
result = Chats.delete_chat_by_id(id)
|
2024-04-27 22:24:59 +00:00
|
|
|
|
return result
|
2025-11-27 18:42:00 +00:00
|
|
|
|
|
|
|
|
|
|
# === 普通用户分支 ===
|
2024-04-27 22:24:59 +00:00
|
|
|
|
else:
|
2025-11-27 18:42:00 +00:00
|
|
|
|
# 权限检查
|
2024-11-17 05:31:57 +00:00
|
|
|
|
if not has_permission(
|
|
|
|
|
|
user.id, "chat.delete", request.app.state.config.USER_PERMISSIONS
|
2024-10-01 16:51:58 +00:00
|
|
|
|
):
|
2024-04-27 22:24:59 +00:00
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
|
|
|
|
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2024-10-15 05:57:11 +00:00
|
|
|
|
chat = Chats.get_chat_by_id(id)
|
2025-11-27 18:42:00 +00:00
|
|
|
|
|
|
|
|
|
|
# 清理孤立标签
|
2024-10-15 05:57:11 +00:00
|
|
|
|
for tag in chat.meta.get("tags", []):
|
2024-10-15 05:59:17 +00:00
|
|
|
|
if Chats.count_chats_by_tag_name_and_user_id(tag, user.id) == 1:
|
2024-10-15 05:57:11 +00:00
|
|
|
|
Tags.delete_tag_by_name_and_user_id(tag, user.id)
|
|
|
|
|
|
|
2025-11-27 18:42:00 +00:00
|
|
|
|
# 删除聊天(带用户 ID 校验)
|
2024-06-21 12:58:57 +00:00
|
|
|
|
result = Chats.delete_chat_by_id_and_user_id(id, user.id)
|
2024-04-27 22:24:59 +00:00
|
|
|
|
return result
|
2023-12-30 08:15:37 +00:00
|
|
|
|
|
2024-01-01 08:55:50 +00:00
|
|
|
|
|
2025-11-27 18:42:00 +00:00
|
|
|
|
|
2024-10-11 06:22:53 +00:00
|
|
|
|
############################
|
|
|
|
|
|
# GetPinnedStatusById
|
|
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/{id}/pinned", response_model=Optional[bool])
|
|
|
|
|
|
async def get_pinned_status_by_id(id: str, user=Depends(get_verified_user)):
|
|
|
|
|
|
chat = Chats.get_chat_by_id_and_user_id(id, user.id)
|
|
|
|
|
|
if chat:
|
|
|
|
|
|
return chat.pinned
|
|
|
|
|
|
else:
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.DEFAULT()
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
############################
|
|
|
|
|
|
# PinChatById
|
|
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/{id}/pin", response_model=Optional[ChatResponse])
|
|
|
|
|
|
async def pin_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_pinned_by_id(id)
|
|
|
|
|
|
return chat
|
|
|
|
|
|
else:
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.DEFAULT()
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-05-31 17:30:42 +00:00
|
|
|
|
############################
|
|
|
|
|
|
# CloneChat
|
|
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-01-28 20:49:06 +00:00
|
|
|
|
class CloneForm(BaseModel):
|
|
|
|
|
|
title: Optional[str] = None
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-10-11 06:22:53 +00:00
|
|
|
|
@router.post("/{id}/clone", response_model=Optional[ChatResponse])
|
2025-01-28 20:49:06 +00:00
|
|
|
|
async def clone_chat_by_id(
|
|
|
|
|
|
form_data: CloneForm, id: str, user=Depends(get_verified_user)
|
|
|
|
|
|
):
|
2024-06-21 12:58:57 +00:00
|
|
|
|
chat = Chats.get_chat_by_id_and_user_id(id, user.id)
|
2024-05-31 17:30:42 +00:00
|
|
|
|
if chat:
|
|
|
|
|
|
updated_chat = {
|
2024-10-09 05:02:48 +00:00
|
|
|
|
**chat.chat,
|
2024-05-31 17:30:42 +00:00
|
|
|
|
"originalChatId": chat.id,
|
2024-10-09 05:02:48 +00:00
|
|
|
|
"branchPointMessageId": chat.chat["history"]["currentId"],
|
2025-01-28 20:49:06 +00:00
|
|
|
|
"title": form_data.title if form_data.title else f"Clone of {chat.title}",
|
2024-05-31 17:30:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-04 20:52:45 +00:00
|
|
|
|
chat = Chats.import_chat(
|
|
|
|
|
|
user.id,
|
|
|
|
|
|
ChatImportForm(
|
|
|
|
|
|
**{
|
|
|
|
|
|
"chat": updated_chat,
|
|
|
|
|
|
"meta": chat.meta,
|
|
|
|
|
|
"pinned": chat.pinned,
|
|
|
|
|
|
"folder_id": chat.folder_id,
|
|
|
|
|
|
}
|
|
|
|
|
|
),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2024-10-09 05:02:48 +00:00
|
|
|
|
return ChatResponse(**chat.model_dump())
|
2024-05-31 17:30:42 +00:00
|
|
|
|
else:
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.DEFAULT()
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-01-03 05:08:49 +00:00
|
|
|
|
############################
|
2025-01-05 08:44:38 +00:00
|
|
|
|
# CloneSharedChatById
|
2025-01-03 05:08:49 +00:00
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-01-05 08:44:38 +00:00
|
|
|
|
@router.post("/{id}/clone/shared", response_model=Optional[ChatResponse])
|
|
|
|
|
|
async def clone_shared_chat_by_id(id: str, user=Depends(get_verified_user)):
|
2025-04-13 01:11:22 +00:00
|
|
|
|
|
|
|
|
|
|
if user.role == "admin":
|
|
|
|
|
|
chat = Chats.get_chat_by_id(id)
|
|
|
|
|
|
else:
|
|
|
|
|
|
chat = Chats.get_chat_by_share_id(id)
|
|
|
|
|
|
|
2025-01-03 05:08:49 +00:00
|
|
|
|
if chat:
|
|
|
|
|
|
updated_chat = {
|
|
|
|
|
|
**chat.chat,
|
|
|
|
|
|
"originalChatId": chat.id,
|
|
|
|
|
|
"branchPointMessageId": chat.chat["history"]["currentId"],
|
|
|
|
|
|
"title": f"Clone of {chat.title}",
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-05 20:21:03 +00:00
|
|
|
|
chat = Chats.import_chat(
|
|
|
|
|
|
user.id,
|
|
|
|
|
|
ChatImportForm(
|
|
|
|
|
|
**{
|
|
|
|
|
|
"chat": updated_chat,
|
|
|
|
|
|
"meta": chat.meta,
|
|
|
|
|
|
"pinned": chat.pinned,
|
|
|
|
|
|
"folder_id": chat.folder_id,
|
|
|
|
|
|
}
|
|
|
|
|
|
),
|
|
|
|
|
|
)
|
2025-01-03 05:08:49 +00:00
|
|
|
|
return ChatResponse(**chat.model_dump())
|
|
|
|
|
|
else:
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.DEFAULT()
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-04-20 22:03:39 +00:00
|
|
|
|
############################
|
|
|
|
|
|
# ArchiveChat
|
|
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-10-11 06:22:53 +00:00
|
|
|
|
@router.post("/{id}/archive", response_model=Optional[ChatResponse])
|
2024-06-27 18:29:59 +00:00
|
|
|
|
async def archive_chat_by_id(id: str, user=Depends(get_verified_user)):
|
2024-06-21 12:58:57 +00:00
|
|
|
|
chat = Chats.get_chat_by_id_and_user_id(id, user.id)
|
2024-04-20 22:03:39 +00:00
|
|
|
|
if chat:
|
2024-06-21 12:58:57 +00:00
|
|
|
|
chat = Chats.toggle_chat_archive_by_id(id)
|
2024-10-15 05:57:11 +00:00
|
|
|
|
|
|
|
|
|
|
# Delete tags if chat is archived
|
|
|
|
|
|
if chat.archived:
|
|
|
|
|
|
for tag_id in chat.meta.get("tags", []):
|
|
|
|
|
|
if Chats.count_chats_by_tag_name_and_user_id(tag_id, user.id) == 0:
|
|
|
|
|
|
log.debug(f"deleting tag: {tag_id}")
|
|
|
|
|
|
Tags.delete_tag_by_name_and_user_id(tag_id, user.id)
|
|
|
|
|
|
else:
|
|
|
|
|
|
for tag_id in chat.meta.get("tags", []):
|
|
|
|
|
|
tag = Tags.get_tag_by_name_and_user_id(tag_id, user.id)
|
|
|
|
|
|
if tag is None:
|
|
|
|
|
|
log.debug(f"inserting tag: {tag_id}")
|
|
|
|
|
|
tag = Tags.insert_new_tag(tag_id, user.id)
|
|
|
|
|
|
|
2024-10-09 05:02:48 +00:00
|
|
|
|
return ChatResponse(**chat.model_dump())
|
2024-04-20 22:03:39 +00:00
|
|
|
|
else:
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.DEFAULT()
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-03-31 21:02:40 +00:00
|
|
|
|
############################
|
|
|
|
|
|
# ShareChatById
|
|
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/{id}/share", response_model=Optional[ChatResponse])
|
2025-04-23 05:43:33 +00:00
|
|
|
|
async def share_chat_by_id(request: Request, id: str, user=Depends(get_verified_user)):
|
2025-06-28 10:02:07 +00:00
|
|
|
|
if (user.role != "admin") and (
|
|
|
|
|
|
not has_permission(
|
|
|
|
|
|
user.id, "chat.share", request.app.state.config.USER_PERMISSIONS
|
|
|
|
|
|
)
|
2025-04-23 05:43:33 +00:00
|
|
|
|
):
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
|
|
|
|
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2024-06-21 12:58:57 +00:00
|
|
|
|
chat = Chats.get_chat_by_id_and_user_id(id, user.id)
|
2025-04-23 05:43:33 +00:00
|
|
|
|
|
2024-03-31 21:02:40 +00:00
|
|
|
|
if chat:
|
|
|
|
|
|
if chat.share_id:
|
2024-06-21 12:58:57 +00:00
|
|
|
|
shared_chat = Chats.update_shared_chat_by_chat_id(chat.id)
|
2024-10-09 05:02:48 +00:00
|
|
|
|
return ChatResponse(**shared_chat.model_dump())
|
2024-03-31 21:02:40 +00:00
|
|
|
|
|
2024-06-21 12:58:57 +00:00
|
|
|
|
shared_chat = Chats.insert_shared_chat_by_chat_id(chat.id)
|
2024-03-31 21:02:40 +00:00
|
|
|
|
if not shared_chat:
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
|
detail=ERROR_MESSAGES.DEFAULT(),
|
|
|
|
|
|
)
|
2024-10-09 05:02:48 +00:00
|
|
|
|
return ChatResponse(**shared_chat.model_dump())
|
2024-03-31 21:02:40 +00:00
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
|
|
|
|
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
############################
|
|
|
|
|
|
# DeletedSharedChatById
|
|
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-04-02 14:16:25 +00:00
|
|
|
|
@router.delete("/{id}/share", response_model=Optional[bool])
|
2024-06-27 18:29:59 +00:00
|
|
|
|
async def delete_shared_chat_by_id(id: str, user=Depends(get_verified_user)):
|
2024-06-21 12:58:57 +00:00
|
|
|
|
chat = Chats.get_chat_by_id_and_user_id(id, user.id)
|
2024-03-31 21:02:40 +00:00
|
|
|
|
if chat:
|
|
|
|
|
|
if not chat.share_id:
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
2024-06-21 12:58:57 +00:00
|
|
|
|
result = Chats.delete_shared_chat_by_chat_id(id)
|
|
|
|
|
|
update_result = Chats.update_chat_share_id_by_id(id, None)
|
2024-04-02 14:16:25 +00:00
|
|
|
|
|
|
|
|
|
|
return result and update_result != None
|
2024-03-31 21:02:40 +00:00
|
|
|
|
else:
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
|
|
|
|
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-10-17 06:45:50 +00:00
|
|
|
|
############################
|
|
|
|
|
|
# UpdateChatFolderIdById
|
|
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ChatFolderIdForm(BaseModel):
|
|
|
|
|
|
folder_id: Optional[str] = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/{id}/folder", response_model=Optional[ChatResponse])
|
|
|
|
|
|
async def update_chat_folder_id_by_id(
|
|
|
|
|
|
id: str, form_data: ChatFolderIdForm, user=Depends(get_verified_user)
|
|
|
|
|
|
):
|
|
|
|
|
|
chat = Chats.get_chat_by_id_and_user_id(id, user.id)
|
|
|
|
|
|
if chat:
|
|
|
|
|
|
chat = Chats.update_chat_folder_id_by_id_and_user_id(
|
|
|
|
|
|
id, user.id, form_data.folder_id
|
|
|
|
|
|
)
|
|
|
|
|
|
return ChatResponse(**chat.model_dump())
|
|
|
|
|
|
else:
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.DEFAULT()
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-01-18 08:58:45 +00:00
|
|
|
|
############################
|
|
|
|
|
|
# GetChatTagsById
|
|
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-08-14 12:46:31 +00:00
|
|
|
|
@router.get("/{id}/tags", response_model=list[TagModel])
|
2024-06-27 18:29:59 +00:00
|
|
|
|
async def get_chat_tags_by_id(id: str, user=Depends(get_verified_user)):
|
2024-10-11 06:22:53 +00:00
|
|
|
|
chat = Chats.get_chat_by_id_and_user_id(id, user.id)
|
|
|
|
|
|
if chat:
|
|
|
|
|
|
tags = chat.meta.get("tags", [])
|
2024-10-13 08:00:38 +00:00
|
|
|
|
return Tags.get_tags_by_ids_and_user_id(tags, user.id)
|
2024-01-18 08:58:45 +00:00
|
|
|
|
else:
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
############################
|
|
|
|
|
|
# AddChatTagById
|
|
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-10-11 06:22:53 +00:00
|
|
|
|
@router.post("/{id}/tags", response_model=list[TagModel])
|
|
|
|
|
|
async def add_tag_by_id_and_tag_name(
|
|
|
|
|
|
id: str, form_data: TagForm, user=Depends(get_verified_user)
|
2024-01-18 08:58:45 +00:00
|
|
|
|
):
|
2024-10-11 06:22:53 +00:00
|
|
|
|
chat = Chats.get_chat_by_id_and_user_id(id, user.id)
|
|
|
|
|
|
if chat:
|
|
|
|
|
|
tags = chat.meta.get("tags", [])
|
|
|
|
|
|
tag_id = form_data.name.replace(" ", "_").lower()
|
2024-01-18 08:58:45 +00:00
|
|
|
|
|
2024-10-20 04:04:56 +00:00
|
|
|
|
if tag_id == "none":
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
|
|
|
|
detail=ERROR_MESSAGES.DEFAULT("Tag name cannot be 'None'"),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2024-10-11 06:22:53 +00:00
|
|
|
|
if tag_id not in tags:
|
|
|
|
|
|
Chats.add_chat_tag_by_id_and_user_id_and_tag_name(
|
|
|
|
|
|
id, user.id, form_data.name
|
2024-01-18 10:10:07 +00:00
|
|
|
|
)
|
2024-10-11 06:22:53 +00:00
|
|
|
|
|
|
|
|
|
|
chat = Chats.get_chat_by_id_and_user_id(id, user.id)
|
|
|
|
|
|
tags = chat.meta.get("tags", [])
|
2024-10-13 08:00:38 +00:00
|
|
|
|
return Tags.get_tags_by_ids_and_user_id(tags, user.id)
|
2024-01-18 08:58:45 +00:00
|
|
|
|
else:
|
|
|
|
|
|
raise HTTPException(
|
2024-01-18 10:10:07 +00:00
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.DEFAULT()
|
2024-01-18 08:58:45 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
############################
|
|
|
|
|
|
# DeleteChatTagById
|
|
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-10-11 06:22:53 +00:00
|
|
|
|
@router.delete("/{id}/tags", response_model=list[TagModel])
|
|
|
|
|
|
async def delete_tag_by_id_and_tag_name(
|
|
|
|
|
|
id: str, form_data: TagForm, user=Depends(get_verified_user)
|
2024-01-18 08:58:45 +00:00
|
|
|
|
):
|
2024-10-11 06:22:53 +00:00
|
|
|
|
chat = Chats.get_chat_by_id_and_user_id(id, user.id)
|
|
|
|
|
|
if chat:
|
|
|
|
|
|
Chats.delete_tag_by_id_and_user_id_and_tag_name(id, user.id, form_data.name)
|
2024-01-18 08:58:45 +00:00
|
|
|
|
|
2024-10-11 06:22:53 +00:00
|
|
|
|
if Chats.count_chats_by_tag_name_and_user_id(form_data.name, user.id) == 0:
|
|
|
|
|
|
Tags.delete_tag_by_name_and_user_id(form_data.name, user.id)
|
|
|
|
|
|
|
|
|
|
|
|
chat = Chats.get_chat_by_id_and_user_id(id, user.id)
|
|
|
|
|
|
tags = chat.meta.get("tags", [])
|
2024-10-13 08:00:38 +00:00
|
|
|
|
return Tags.get_tags_by_ids_and_user_id(tags, user.id)
|
2024-01-18 09:04:24 +00:00
|
|
|
|
else:
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
############################
|
2024-10-21 08:30:22 +00:00
|
|
|
|
# DeleteAllTagsById
|
2024-01-18 09:04:24 +00:00
|
|
|
|
############################
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.delete("/{id}/tags/all", response_model=Optional[bool])
|
2024-10-21 08:30:22 +00:00
|
|
|
|
async def delete_all_tags_by_id(id: str, user=Depends(get_verified_user)):
|
2024-10-11 06:22:53 +00:00
|
|
|
|
chat = Chats.get_chat_by_id_and_user_id(id, user.id)
|
|
|
|
|
|
if chat:
|
|
|
|
|
|
Chats.delete_all_tags_by_id_and_user_id(id, user.id)
|
2024-01-18 09:04:24 +00:00
|
|
|
|
|
2024-10-11 06:22:53 +00:00
|
|
|
|
for tag in chat.meta.get("tags", []):
|
|
|
|
|
|
if Chats.count_chats_by_tag_name_and_user_id(tag, user.id) == 0:
|
|
|
|
|
|
Tags.delete_tag_by_name_and_user_id(tag, user.id)
|
|
|
|
|
|
|
2024-10-21 08:30:22 +00:00
|
|
|
|
return True
|
2024-01-18 08:58:45 +00:00
|
|
|
|
else:
|
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND
|
|
|
|
|
|
)
|