application_chat_record.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. # coding=utf-8
  2. """
  3. @project: MaxKB
  4. @Author:虎虎
  5. @file: application_chat_record.py
  6. @date:2025/6/10 15:10
  7. @desc:
  8. """
  9. from functools import reduce
  10. from typing import Dict
  11. import uuid_utils.compat as uuid
  12. from django.db import transaction
  13. from django.db.models import QuerySet
  14. from django.db.models.aggregates import Max, Min
  15. from django.utils.translation import gettext_lazy as _, gettext
  16. from rest_framework import serializers
  17. from rest_framework.utils.formatting import lazy_format
  18. from application.models import ChatRecord, ApplicationAccessToken, Application
  19. from application.serializers.application_chat import ChatCountSerializer
  20. from application.serializers.common import ChatInfo
  21. from common.auth.authentication import get_is_permissions
  22. from common.constants.permission_constants import PermissionConstants, RoleConstants, ViewPermission, CompareConstants
  23. from common.db.search import page_search
  24. from common.exception.app_exception import AppApiException, AppUnauthorizedFailed
  25. from common.utils.common import post
  26. from knowledge.models import Paragraph, Document, Problem, ProblemParagraphMapping, Knowledge
  27. from knowledge.serializers.common import get_embedding_model_id_by_knowledge_id, update_document_char_length
  28. from knowledge.serializers.paragraph import ParagraphSerializers
  29. from knowledge.task.embedding import embedding_by_paragraph, embedding_by_paragraph_list
  30. class ChatRecordSerializerModel(serializers.ModelSerializer):
  31. class Meta:
  32. model = ChatRecord
  33. fields = ['id', 'chat_id', 'vote_status','vote_reason','vote_other_content', 'problem_text', 'answer_text',
  34. 'message_tokens', 'answer_tokens', 'const', 'improve_paragraph_id_list', 'run_time', 'index',
  35. 'answer_text_list',
  36. 'create_time', 'update_time']
  37. class ChatRecordOperateSerializer(serializers.Serializer):
  38. chat_id = serializers.UUIDField(required=True, label=_("Conversation ID"))
  39. workspace_id = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_("Workspace ID"))
  40. application_id = serializers.UUIDField(required=True, label=_("Application ID"))
  41. chat_record_id = serializers.UUIDField(required=True, label=_("Conversation record id"))
  42. def is_valid(self, *, debug=False, raise_exception=False):
  43. super().is_valid(raise_exception=True)
  44. workspace_id = self.data.get('workspace_id')
  45. query_set = QuerySet(Application).filter(id=self.data.get('application_id'))
  46. if workspace_id:
  47. query_set = query_set.filter(workspace_id=workspace_id)
  48. if not query_set.exists():
  49. raise AppApiException(500, _('Application id does not exist'))
  50. application_access_token = QuerySet(ApplicationAccessToken).filter(
  51. application_id=self.data.get('application_id')).first()
  52. if application_access_token is None:
  53. raise AppApiException(500, gettext('Application authentication information does not exist'))
  54. def get_chat_record(self):
  55. chat_record_id = self.data.get('chat_record_id')
  56. chat_id = self.data.get('chat_id')
  57. chat_info: ChatInfo = ChatInfo.get_cache(chat_id)
  58. if chat_info is not None:
  59. chat_record_list = [chat_record for chat_record in chat_info.chat_record_list if
  60. str(chat_record.id) == str(chat_record_id)]
  61. if chat_record_list is not None and len(chat_record_list):
  62. return chat_record_list[-1]
  63. return QuerySet(ChatRecord).filter(id=chat_record_id, chat_id=chat_id).first()
  64. def one(self, debug):
  65. self.is_valid(debug=debug, raise_exception=True)
  66. chat_record = self.get_chat_record()
  67. if chat_record is None:
  68. raise AppApiException(500, gettext("Conversation does not exist"))
  69. application_access_token = QuerySet(ApplicationAccessToken).filter(
  70. application_id=self.data.get('application_id')).first()
  71. show_source = False
  72. show_exec = False
  73. if application_access_token is not None:
  74. show_exec = application_access_token.show_exec
  75. show_source = application_access_token.show_source
  76. return ApplicationChatRecordQuerySerializers.reset_chat_record(
  77. chat_record, True if debug else show_source, True if debug else show_exec)
  78. class ApplicationChatRecordQuerySerializers(serializers.Serializer):
  79. workspace_id = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_("Workspace ID"))
  80. application_id = serializers.UUIDField(required=True, label=_("Application ID"))
  81. chat_id = serializers.UUIDField(required=True, label=_("Chat ID"))
  82. order_asc = serializers.BooleanField(required=False, allow_null=True, label=_("Is it in order"))
  83. def is_valid(self, *, raise_exception=False):
  84. super().is_valid(raise_exception=True)
  85. workspace_id = self.data.get('workspace_id')
  86. query_set = QuerySet(Application).filter(id=self.data.get('application_id'))
  87. if workspace_id:
  88. query_set = query_set.filter(workspace_id=workspace_id)
  89. if not query_set.exists():
  90. raise AppApiException(500, _('Application id does not exist'))
  91. def list(self, with_valid=True):
  92. if with_valid:
  93. self.is_valid(raise_exception=True)
  94. QuerySet(ChatRecord).filter(chat_id=self.data.get('chat_id'))
  95. order_by = 'create_time' if self.data.get('order_asc') is None or self.data.get(
  96. 'order_asc') else '-create_time'
  97. return [ChatRecordSerializerModel(chat_record).data for chat_record in
  98. QuerySet(ChatRecord).filter(chat_id=self.data.get('chat_id')).order_by(order_by)]
  99. @staticmethod
  100. def get_loop_workflow_node(details):
  101. result = []
  102. for item in details.values():
  103. if item.get('type') == 'loop-node':
  104. for loop_item in item.get('loop_node_data') or []:
  105. for inner_item in loop_item.values():
  106. result.append(inner_item)
  107. return result
  108. @staticmethod
  109. def reset_chat_record(chat_record, show_source, show_exec):
  110. knowledge_list = []
  111. paragraph_list = []
  112. if 'search_step' in chat_record.details and chat_record.details.get('search_step').get(
  113. 'paragraph_list') is not None:
  114. paragraph_list = chat_record.details.get('search_step').get(
  115. 'paragraph_list')
  116. for item in [*chat_record.details.values(),
  117. *ApplicationChatRecordQuerySerializers.get_loop_workflow_node(chat_record.details)]:
  118. if item.get('type') == 'search-knowledge-node' and item.get('show_knowledge', False):
  119. paragraph_list = paragraph_list + (item.get(
  120. 'paragraph_list') or [])
  121. if item.get('type') == 'reranker-node' and item.get('show_knowledge', False):
  122. paragraph_list = paragraph_list + [rl.get('metadata') for rl in (item.get('result_list') or []) if
  123. 'document_id' in (rl.get('metadata') or {}) and 'knowledge_id' in (
  124. rl.get(
  125. 'metadata') or {})]
  126. paragraph_list = list({p.get('id'): p for p in paragraph_list}.values())
  127. knowledge_list = knowledge_list + [{'id': knowledge_id, **knowledge} for knowledge_id, knowledge in
  128. reduce(lambda x, y: {**x, **y},
  129. [{row.get(
  130. 'knowledge_id'): {'knowledge_name': row.get(
  131. "knowledge_name"),
  132. 'knowledge_type': row.get('knowledge_type')}} for
  133. row in
  134. paragraph_list],
  135. {}).items()]
  136. if len(chat_record.improve_paragraph_id_list) > 0:
  137. paragraph_model_list = QuerySet(Paragraph).filter(id__in=chat_record.improve_paragraph_id_list)
  138. if len(paragraph_model_list) < len(chat_record.improve_paragraph_id_list):
  139. paragraph_model_id_list = [str(p.id) for p in paragraph_model_list]
  140. chat_record.improve_paragraph_id_list = list(
  141. filter(lambda p_id: paragraph_model_id_list.__contains__(p_id),
  142. chat_record.improve_paragraph_id_list))
  143. chat_record.save()
  144. show_source_dict = {'knowledge_list': knowledge_list,
  145. 'paragraph_list': paragraph_list, }
  146. show_exec_dict = {'execution_details': [chat_record.details[key] for key in chat_record.details if
  147. (True if show_exec else chat_record.details[key].get(
  148. 'type') == 'start-node')]}
  149. return {
  150. **ChatRecordSerializerModel(chat_record).data,
  151. 'padding_problem_text': chat_record.details.get('problem_padding').get(
  152. 'padding_problem_text') if 'problem_padding' in chat_record.details else None,
  153. **(show_source_dict if show_source else {}),
  154. **(show_exec_dict if show_exec else {})
  155. }
  156. def page(self, current_page: int, page_size: int, with_valid=True, show_source=None, show_exec=None):
  157. if with_valid:
  158. self.is_valid(raise_exception=True)
  159. order_by = '-create_time' if self.data.get('order_asc') is None or self.data.get(
  160. 'order_asc') else 'create_time'
  161. if show_source is None:
  162. show_source = True
  163. if show_exec is None:
  164. show_exec = True
  165. page = page_search(current_page, page_size,
  166. QuerySet(ChatRecord).filter(chat_id=self.data.get('chat_id')).order_by(order_by),
  167. post_records_handler=lambda chat_record: self.reset_chat_record(chat_record, show_source,
  168. show_exec))
  169. return page
  170. class ParagraphModel(serializers.ModelSerializer):
  171. class Meta:
  172. model = Paragraph
  173. fields = "__all__"
  174. class ChatRecordImproveSerializer(serializers.Serializer):
  175. workspace_id = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_("Workspace ID"))
  176. application_id = serializers.UUIDField(required=True, label=_("Application ID"))
  177. chat_id = serializers.UUIDField(required=True, label=_("Conversation ID"))
  178. chat_record_id = serializers.UUIDField(required=True,
  179. label=_("Conversation record id"))
  180. def is_valid(self, *, raise_exception=False):
  181. super().is_valid(raise_exception=True)
  182. workspace_id = self.data.get('workspace_id')
  183. query_set = QuerySet(Application).filter(id=self.data.get('application_id'))
  184. if workspace_id:
  185. query_set = query_set.filter(workspace_id=workspace_id)
  186. if not query_set.exists():
  187. raise AppApiException(500, _('Application id does not exist'))
  188. def get(self, with_valid=True):
  189. if with_valid:
  190. self.is_valid(raise_exception=True)
  191. chat_record_id = self.data.get('chat_record_id')
  192. chat_id = self.data.get('chat_id')
  193. chat_record = QuerySet(ChatRecord).filter(id=chat_record_id, chat_id=chat_id).first()
  194. if chat_record is None:
  195. raise AppApiException(500, gettext('Conversation record does not exist'))
  196. if chat_record.improve_paragraph_id_list is None or len(chat_record.improve_paragraph_id_list) == 0:
  197. return []
  198. paragraph_model_list = QuerySet(Paragraph).filter(id__in=chat_record.improve_paragraph_id_list)
  199. if len(paragraph_model_list) < len(chat_record.improve_paragraph_id_list):
  200. paragraph_model_id_list = [str(p.id) for p in paragraph_model_list]
  201. chat_record.improve_paragraph_id_list = list(
  202. filter(lambda p_id: paragraph_model_id_list.__contains__(p_id),
  203. chat_record.improve_paragraph_id_list))
  204. chat_record.save()
  205. return [ParagraphModel(p).data for p in paragraph_model_list]
  206. class ApplicationChatRecordImproveInstanceSerializer(serializers.Serializer):
  207. title = serializers.CharField(required=False, max_length=256, allow_null=True, allow_blank=True,
  208. label=_("Section title"))
  209. content = serializers.CharField(required=True, label=_("Paragraph content"))
  210. problem_text = serializers.CharField(required=False, max_length=256, allow_null=True, allow_blank=True,
  211. label=_("question"))
  212. class ApplicationChatRecordAddKnowledgeSerializer(serializers.Serializer):
  213. workspace_id = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_("Workspace ID"))
  214. application_id = serializers.UUIDField(required=True, label=_("Application ID"))
  215. knowledge_id = serializers.UUIDField(required=True, label=_("Knowledge base id"))
  216. document_id = serializers.UUIDField(required=True, label=_("Document id"))
  217. chat_ids = serializers.ListSerializer(child=serializers.UUIDField(), required=True,
  218. label=_("Conversation ID"))
  219. def is_valid(self, *, raise_exception=False):
  220. super().is_valid(raise_exception=True)
  221. workspace_id = self.data.get('workspace_id')
  222. query_set = QuerySet(Application).filter(id=self.data.get('application_id'))
  223. if workspace_id:
  224. query_set = query_set.filter(workspace_id=workspace_id)
  225. if not query_set.exists():
  226. raise AppApiException(500, _('Application id does not exist'))
  227. if not Document.objects.filter(id=self.data['document_id'], knowledge_id=self.data['knowledge_id']).exists():
  228. raise AppApiException(500, gettext("The document id is incorrect"))
  229. @staticmethod
  230. def post_embedding_paragraph(paragraph_ids, knowledge_id):
  231. model_id = get_embedding_model_id_by_knowledge_id(knowledge_id)
  232. embedding_by_paragraph_list(paragraph_ids, model_id)
  233. @post(post_function=post_embedding_paragraph)
  234. @transaction.atomic
  235. def post_improve(self, instance: Dict, request=None, scope='WORKSPACE', with_valid=True):
  236. if with_valid:
  237. ApplicationChatRecordAddKnowledgeSerializer(data=instance).is_valid(raise_exception=True)
  238. self.is_valid(raise_exception=True)
  239. if scope == 'WORKSPACE':
  240. is_permission = get_is_permissions(request=request, workspace_id=self.data.get('workspace_id'),
  241. knowledge_id=self.data.get("knowledge_id"))(
  242. PermissionConstants.KNOWLEDGE_DOCUMENT_EDIT.get_workspace_knowledge_permission(),
  243. PermissionConstants.KNOWLEDGE_DOCUMENT_EDIT.get_workspace_permission_workspace_manage_role(),
  244. RoleConstants.WORKSPACE_MANAGE.get_workspace_role(),
  245. ViewPermission([RoleConstants.USER.get_workspace_role()],
  246. [PermissionConstants.KNOWLEDGE.get_workspace_knowledge_permission()],
  247. CompareConstants.AND),
  248. )
  249. else:
  250. is_permission = get_is_permissions(request=request, workspace_id=self.data.get('workspace_id'),
  251. knowledge_id=self.data.get("knowledge_id"))(
  252. PermissionConstants.RESOURCE_KNOWLEDGE_DOCUMENT_EDIT, RoleConstants.ADMIN
  253. )
  254. if not is_permission:
  255. raise AppUnauthorizedFailed(403, gettext('No permission to access'))
  256. chat_ids = instance['chat_ids']
  257. document_id = instance['document_id']
  258. knowledge_id = instance['knowledge_id']
  259. # 获取所有聊天记录
  260. chat_record_list = list(ChatRecord.objects.filter(chat_id__in=chat_ids))
  261. if len(chat_record_list) < len(chat_ids):
  262. raise AppApiException(500, gettext("Conversation records that do not exist"))
  263. # 批量创建段落和问题映射
  264. paragraphs = []
  265. paragraph_ids = []
  266. problem_paragraph_mappings = []
  267. for chat_record in chat_record_list:
  268. paragraph = Paragraph(
  269. id=uuid.uuid7(),
  270. document_id=document_id,
  271. content=chat_record.answer_text,
  272. knowledge_id=knowledge_id,
  273. title=chat_record.problem_text
  274. )
  275. problem, _ = Problem.objects.get_or_create(content=chat_record.problem_text, knowledge_id=knowledge_id)
  276. problem_paragraph_mapping = ProblemParagraphMapping(
  277. id=uuid.uuid7(),
  278. knowledge_id=knowledge_id,
  279. document_id=document_id,
  280. problem_id=problem.id,
  281. paragraph_id=paragraph.id
  282. )
  283. paragraphs.append(paragraph)
  284. paragraph_ids.append(paragraph.id)
  285. problem_paragraph_mappings.append(problem_paragraph_mapping)
  286. chat_record.improve_paragraph_id_list.append(paragraph.id)
  287. # 处理段落位置
  288. self.prepend_paragraphs(document_id, paragraphs)
  289. # 批量创建新段落和问题映射
  290. Paragraph.objects.bulk_create(paragraphs)
  291. ProblemParagraphMapping.objects.bulk_create(problem_paragraph_mappings)
  292. # 批量保存聊天记录
  293. ChatRecord.objects.bulk_update(chat_record_list, ['improve_paragraph_id_list'])
  294. update_document_char_length(document_id)
  295. for chat_id in chat_ids:
  296. ChatCountSerializer(data={'chat_id': chat_id}).update_chat()
  297. return paragraph_ids, knowledge_id
  298. @staticmethod
  299. def prepend_paragraphs(document_id, paragraphs):
  300. # 获取所有现有段落
  301. existing_paragraphs = list(Paragraph.objects.filter(
  302. document_id=document_id
  303. ).order_by('position'))
  304. # 计算新段落数量
  305. new_count = len(paragraphs)
  306. # 如果已有段落,需要重新调整所有段落的位置
  307. if existing_paragraphs:
  308. # 为现有段落重新分配位置,从新段落数量+1开始
  309. for i, existing_paragraph in enumerate(existing_paragraphs):
  310. existing_paragraph.position = new_count + i + 1
  311. # 批量更新现有段落位置
  312. if existing_paragraphs:
  313. Paragraph.objects.bulk_update(existing_paragraphs, ['position'])
  314. # 为新段落分配位置,从1开始
  315. for i, paragraph in enumerate(paragraphs):
  316. paragraph.position = i + 1
  317. class ApplicationChatRecordImproveSerializer(serializers.Serializer):
  318. chat_id = serializers.UUIDField(required=True, label=_("Conversation ID"))
  319. chat_record_id = serializers.UUIDField(required=True,
  320. label=_("Conversation record id"))
  321. knowledge_id = serializers.UUIDField(required=True, label=_("Knowledge base id"))
  322. document_id = serializers.UUIDField(required=True, label=_("Document id"))
  323. application_id = serializers.UUIDField(required=True, label=_("Application id"))
  324. workspace_id = serializers.CharField(required=True, label=_("Workspace ID"))
  325. def is_valid(self, *, raise_exception=False):
  326. super().is_valid(raise_exception=True)
  327. workspace_id = self.data.get('workspace_id')
  328. query_set = QuerySet(Application).filter(id=self.data.get('application_id'))
  329. if workspace_id:
  330. query_set = query_set.filter(workspace_id=workspace_id)
  331. if not query_set.exists():
  332. raise AppApiException(500, _('Application id does not exist'))
  333. query_set = QuerySet(Knowledge).filter(id=self.data.get('knowledge_id'))
  334. if workspace_id:
  335. query_set = query_set.filter(workspace_id=workspace_id)
  336. if not query_set.exists():
  337. raise AppApiException(500, _('Knowledge id does not exist'))
  338. if not QuerySet(Document).filter(id=self.data.get('document_id'),
  339. knowledge_id=self.data.get('knowledge_id')).exists():
  340. raise AppApiException(500, gettext("The document id is incorrect"))
  341. @staticmethod
  342. def post_embedding_paragraph(chat_record, paragraph_id, knowledge_id):
  343. model_id = get_embedding_model_id_by_knowledge_id(knowledge_id)
  344. # 发送向量化事件
  345. embedding_by_paragraph(paragraph_id, model_id)
  346. return chat_record
  347. @post(post_function=post_embedding_paragraph)
  348. @transaction.atomic
  349. def improve(self, instance: Dict, request=None, scope='WORKSPACE', with_valid=True):
  350. if with_valid:
  351. self.is_valid(raise_exception=True)
  352. if scope == 'WORKSPACE':
  353. is_permission = get_is_permissions(request, workspace_id=self.data.get('workspace_id'),
  354. knowledge_id=self.data.get("knowledge_id"))(
  355. PermissionConstants.KNOWLEDGE_DOCUMENT_EDIT.get_workspace_knowledge_permission(),
  356. PermissionConstants.KNOWLEDGE_DOCUMENT_EDIT.get_workspace_permission_workspace_manage_role(),
  357. RoleConstants.WORKSPACE_MANAGE.get_workspace_role(),
  358. ViewPermission([RoleConstants.USER.get_workspace_role()],
  359. [PermissionConstants.KNOWLEDGE.get_workspace_knowledge_permission()],
  360. CompareConstants.AND),
  361. )
  362. else:
  363. is_permission = get_is_permissions(request, workspace_id=self.data.get('workspace_id'),
  364. knowledge_id=self.data.get("knowledge_id"))(
  365. PermissionConstants.RESOURCE_KNOWLEDGE_DOCUMENT_EDIT, RoleConstants.ADMIN
  366. )
  367. if not is_permission:
  368. raise AppUnauthorizedFailed(403, gettext('No permission to access'))
  369. ApplicationChatRecordImproveInstanceSerializer(data=instance).is_valid(raise_exception=True)
  370. chat_record_id = self.data.get('chat_record_id')
  371. chat_id = self.data.get('chat_id')
  372. chat_record = QuerySet(ChatRecord).filter(id=chat_record_id, chat_id=chat_id).first()
  373. if chat_record is None:
  374. raise AppApiException(500, gettext('Conversation record does not exist'))
  375. document_id = self.data.get("document_id")
  376. knowledge_id = self.data.get("knowledge_id")
  377. max_position = Paragraph.objects.filter(document_id=document_id).aggregate(
  378. max_position=Max('position')
  379. )['max_position'] or 0
  380. paragraph = Paragraph(
  381. id=uuid.uuid7(),
  382. document_id=document_id,
  383. content=instance.get("content"),
  384. knowledge_id=knowledge_id,
  385. title=instance.get("title") if 'title' in instance else '',
  386. position=max_position + 1
  387. )
  388. problem_text = instance.get('problem_text') if instance.get(
  389. 'problem_text') is not None else chat_record.problem_text
  390. problem, _ = QuerySet(Problem).get_or_create(content=problem_text, knowledge_id=knowledge_id)
  391. problem_paragraph_mapping = ProblemParagraphMapping(id=uuid.uuid7(), knowledge_id=knowledge_id,
  392. document_id=document_id,
  393. problem_id=problem.id,
  394. paragraph_id=paragraph.id)
  395. # 插入段落
  396. paragraph.save()
  397. # 插入关联问题
  398. problem_paragraph_mapping.save()
  399. chat_record.improve_paragraph_id_list.append(paragraph.id)
  400. update_document_char_length(document_id)
  401. # 添加标注
  402. chat_record.save()
  403. ChatCountSerializer(data={'chat_id': chat_id}).update_chat()
  404. return ChatRecordSerializerModel(chat_record).data, paragraph.id, knowledge_id
  405. class Operate(serializers.Serializer):
  406. chat_id = serializers.UUIDField(required=True, label=_("Conversation ID"))
  407. chat_record_id = serializers.UUIDField(required=True,
  408. label=_("Conversation record id"))
  409. knowledge_id = serializers.UUIDField(required=True, label=_("Knowledge base id"))
  410. document_id = serializers.UUIDField(required=True, label=_("Document id"))
  411. paragraph_id = serializers.UUIDField(required=True, label=_("Paragraph id"))
  412. workspace_id = serializers.CharField(required=True, label=_("Workspace ID"))
  413. def delete(self, request=None, scope='WORKSPACE', with_valid=True):
  414. if with_valid:
  415. self.is_valid(raise_exception=True)
  416. if scope == 'WORKSPACE':
  417. is_permission = get_is_permissions(request=request, workspace_id=self.data.get('workspace_id'),
  418. knowledge_id=self.data.get("knowledge_id"))(
  419. PermissionConstants.KNOWLEDGE_DOCUMENT_EDIT.get_workspace_knowledge_permission(),
  420. PermissionConstants.KNOWLEDGE_DOCUMENT_EDIT.get_workspace_permission_workspace_manage_role(),
  421. RoleConstants.WORKSPACE_MANAGE.get_workspace_role(),
  422. ViewPermission([RoleConstants.USER.get_workspace_role()],
  423. [PermissionConstants.KNOWLEDGE.get_workspace_knowledge_permission()],
  424. CompareConstants.AND),
  425. )
  426. else:
  427. is_permission = get_is_permissions(request=request, workspace_id=self.data.get('workspace_id'),
  428. knowledge_id=self.data.get("knowledge_id"))(
  429. PermissionConstants.RESOURCE_KNOWLEDGE_DOCUMENT_EDIT, RoleConstants.ADMIN
  430. )
  431. if not is_permission:
  432. raise AppUnauthorizedFailed(403, gettext('No permission to access'))
  433. workspace_id = self.data.get('workspace_id')
  434. chat_record_id = self.data.get('chat_record_id')
  435. chat_id = self.data.get('chat_id')
  436. knowledge_id = self.data.get('knowledge_id')
  437. document_id = self.data.get('document_id')
  438. paragraph_id = self.data.get('paragraph_id')
  439. chat_record = QuerySet(ChatRecord).filter(id=chat_record_id, chat_id=chat_id).first()
  440. if chat_record is None:
  441. raise AppApiException(500, gettext('Conversation record does not exist'))
  442. if not chat_record.improve_paragraph_id_list.__contains__(uuid.UUID(paragraph_id)):
  443. message = lazy_format(
  444. gettext(
  445. 'The paragraph id is wrong. The current conversation record does not exist. [{paragraph_id}] paragraph id'),
  446. paragraph_id=paragraph_id)
  447. raise AppApiException(500, message.__str__())
  448. chat_record.improve_paragraph_id_list = [row for row in chat_record.improve_paragraph_id_list if
  449. str(row) != paragraph_id]
  450. chat_record.save()
  451. o = ParagraphSerializers.Operate(
  452. data={"workspace_id": workspace_id, "knowledge_id": knowledge_id, 'document_id': document_id,
  453. "paragraph_id": paragraph_id})
  454. o.is_valid(raise_exception=True)
  455. o.delete()
  456. return True