浏览代码

fix delete conversations via Api and delete conversations from db as well (#23591)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: crazywoola <427733928@qq.com>
Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
tags/1.8.0
znn 2 个月前
父节点
当前提交
3aedc139ac
没有帐户链接到提交者的电子邮件

+ 1
- 1
api/README.md 查看文件

1. If you need to handle and debug the async tasks (e.g. dataset importing and documents indexing), please start the worker service. 1. If you need to handle and debug the async tasks (e.g. dataset importing and documents indexing), please start the worker service.


```bash ```bash
uv run celery -A app.celery worker -P gevent -c 1 --loglevel INFO -Q dataset,generation,mail,ops_trace,app_deletion,plugin,workflow_storage
uv run celery -A app.celery worker -P gevent -c 1 --loglevel INFO -Q dataset,generation,mail,ops_trace,app_deletion,plugin,workflow_storage,conversation
``` ```


Addition, if you want to debug the celery scheduled tasks, you can use the following command in another terminal: Addition, if you want to debug the celery scheduled tasks, you can use the following command in another terminal:

+ 12
- 22
api/controllers/console/app/conversation.py 查看文件

from libs.login import login_required from libs.login import login_required
from models import Conversation, EndUser, Message, MessageAnnotation from models import Conversation, EndUser, Message, MessageAnnotation
from models.model import AppMode from models.model import AppMode
from services.conversation_service import ConversationService
from services.errors.conversation import ConversationNotExistsError




class CompletionConversationApi(Resource): class CompletionConversationApi(Resource):
parser.add_argument("limit", type=int_range(1, 100), default=20, location="args") parser.add_argument("limit", type=int_range(1, 100), default=20, location="args")
args = parser.parse_args() args = parser.parse_args()


query = db.select(Conversation).where(Conversation.app_id == app_model.id, Conversation.mode == "completion")
query = db.select(Conversation).where(
Conversation.app_id == app_model.id, Conversation.mode == "completion", Conversation.is_deleted.is_(False)
)


if args["keyword"]: if args["keyword"]:
query = query.join(Message, Message.conversation_id == Conversation.id).where( query = query.join(Message, Message.conversation_id == Conversation.id).where(
raise Forbidden() raise Forbidden()
conversation_id = str(conversation_id) conversation_id = str(conversation_id)


conversation = (
db.session.query(Conversation)
.where(Conversation.id == conversation_id, Conversation.app_id == app_model.id)
.first()
)

if not conversation:
try:
ConversationService.delete(app_model, conversation_id, current_user)
except ConversationNotExistsError:
raise NotFound("Conversation Not Exists.") raise NotFound("Conversation Not Exists.")


conversation.is_deleted = True
db.session.commit()

return {"result": "success"}, 204 return {"result": "success"}, 204




.subquery() .subquery()
) )


query = db.select(Conversation).where(Conversation.app_id == app_model.id)
query = db.select(Conversation).where(Conversation.app_id == app_model.id, Conversation.is_deleted.is_(False))


if args["keyword"]: if args["keyword"]:
keyword_filter = f"%{args['keyword']}%" keyword_filter = f"%{args['keyword']}%"
raise Forbidden() raise Forbidden()
conversation_id = str(conversation_id) conversation_id = str(conversation_id)


conversation = (
db.session.query(Conversation)
.where(Conversation.id == conversation_id, Conversation.app_id == app_model.id)
.first()
)

if not conversation:
try:
ConversationService.delete(app_model, conversation_id, current_user)
except ConversationNotExistsError:
raise NotFound("Conversation Not Exists.") raise NotFound("Conversation Not Exists.")


conversation.is_deleted = True
db.session.commit()

return {"result": "success"}, 204 return {"result": "success"}, 204





+ 1
- 1
api/docker/entrypoint.sh 查看文件



exec celery -A app.celery worker -P ${CELERY_WORKER_CLASS:-gevent} $CONCURRENCY_OPTION \ exec celery -A app.celery worker -P ${CELERY_WORKER_CLASS:-gevent} $CONCURRENCY_OPTION \
--max-tasks-per-child ${MAX_TASK_PRE_CHILD:-50} --loglevel ${LOG_LEVEL:-INFO} \ --max-tasks-per-child ${MAX_TASK_PRE_CHILD:-50} --loglevel ${LOG_LEVEL:-INFO} \
-Q ${CELERY_QUEUES:-dataset,mail,ops_trace,app_deletion,plugin,workflow_storage}
-Q ${CELERY_QUEUES:-dataset,mail,ops_trace,app_deletion,plugin,workflow_storage,conversation}


elif [[ "${MODE}" == "beat" ]]; then elif [[ "${MODE}" == "beat" ]]; then
exec celery -A app.celery beat --loglevel ${LOG_LEVEL:-INFO} exec celery -A app.celery beat --loglevel ${LOG_LEVEL:-INFO}

+ 18
- 4
api/services/conversation_service.py 查看文件

import contextlib import contextlib
import logging
from collections.abc import Callable, Sequence from collections.abc import Callable, Sequence
from typing import Any, Optional, Union from typing import Any, Optional, Union


LastConversationNotExistsError, LastConversationNotExistsError,
) )
from services.errors.message import MessageNotExistsError from services.errors.message import MessageNotExistsError
from tasks.delete_conversation_task import delete_conversation_related_data

logger = logging.getLogger(__name__)




class ConversationService: class ConversationService:


@classmethod @classmethod
def delete(cls, app_model: App, conversation_id: str, user: Optional[Union[Account, EndUser]]): def delete(cls, app_model: App, conversation_id: str, user: Optional[Union[Account, EndUser]]):
conversation = cls.get_conversation(app_model, conversation_id, user)
try:
logger.info(
"Initiating conversation deletion for app_name %s, conversation_id: %s",
app_model.name,
conversation_id,
)


conversation.is_deleted = True
conversation.updated_at = naive_utc_now()
db.session.commit()
db.session.query(Conversation).where(Conversation.id == conversation_id).delete(synchronize_session=False)
db.session.commit()

delete_conversation_related_data.delay(conversation_id)

except Exception as e:
db.session.rollback()
raise e


@classmethod @classmethod
def get_conversational_variable( def get_conversational_variable(

+ 68
- 0
api/tasks/delete_conversation_task.py 查看文件

import logging
import time

import click
from celery import shared_task # type: ignore

from extensions.ext_database import db
from models import ConversationVariable
from models.model import Message, MessageAnnotation, MessageFeedback
from models.tools import ToolConversationVariables, ToolFile
from models.web import PinnedConversation


@shared_task(queue="conversation")
def delete_conversation_related_data(conversation_id: str) -> None:
"""
Delete related data conversation in correct order from datatbase to respect foreign key constraints

Args:
conversation_id: conversation Id
"""

logging.info(
click.style(f"Starting to delete conversation data from db for conversation_id {conversation_id}", fg="green")
)
start_at = time.perf_counter()

try:
db.session.query(MessageAnnotation).where(MessageAnnotation.conversation_id == conversation_id).delete(
synchronize_session=False
)

db.session.query(MessageFeedback).where(MessageFeedback.conversation_id == conversation_id).delete(
synchronize_session=False
)

db.session.query(ToolConversationVariables).where(
ToolConversationVariables.conversation_id == conversation_id
).delete(synchronize_session=False)

db.session.query(ToolFile).where(ToolFile.conversation_id == conversation_id).delete(synchronize_session=False)

db.session.query(ConversationVariable).where(ConversationVariable.conversation_id == conversation_id).delete(
synchronize_session=False
)

db.session.query(Message).where(Message.conversation_id == conversation_id).delete(synchronize_session=False)

db.session.query(PinnedConversation).where(PinnedConversation.conversation_id == conversation_id).delete(
synchronize_session=False
)

db.session.commit()

end_at = time.perf_counter()
logging.info(
click.style(
f"Succeeded cleaning data from db for conversation_id {conversation_id} latency: {end_at - start_at}",
fg="green",
)
)

except Exception as e:
logging.exception("Failed to delete data from db for conversation_id: %s failed", conversation_id)
db.session.rollback()
raise e
finally:
db.session.close()

正在加载...
取消
保存