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
18 changes: 18 additions & 0 deletions apps/application/migrations/0015_chat_execute_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.2.14 on 2026-07-21 08:57

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('application', '0014_chatrecord_workflow_context'),
]

operations = [
migrations.AddField(
model_name='chat',
name='execute_type',
field=models.CharField(choices=[('ANONYMOUS_USER', '匿名用户'), ('CHAT_USER', '对话用户'), ('SYSTEM_API_KEY', '系统API_KEY'), ('APPLICATION_API_KEY', '应用API_KEY'), ('PLATFORM_USER', '平台用户')], default='CHAT', max_length=64, verbose_name='执行类型'),
),
]
15 changes: 12 additions & 3 deletions apps/application/models/application_chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ class ChatUserType(models.TextChoices):
PLATFORM_USER = "PLATFORM_USER", "平台用户"


class ExecuteType(models.TextChoices):
DEBUG = "DEBUG"
CHAT = "CHAT"


def default_asker():
return {'username': '游客'}

Expand All @@ -37,6 +42,8 @@ class Chat(AppModelMixin):
chat_user_id = models.CharField(verbose_name="对话用户id", default=None, null=True)
chat_user_type = models.CharField(max_length=64, verbose_name="客户端类型", choices=ChatUserType.choices,
default=ChatUserType.ANONYMOUS_USER)
execute_type = models.CharField(max_length=64, verbose_name="执行类型", choices=ChatUserType.choices,
default=ExecuteType.CHAT)
is_deleted = models.BooleanField(verbose_name="逻辑删除", default=False)
asker = models.JSONField(verbose_name="访问者", default=default_asker, encoder=SystemEncoder)
meta = models.JSONField(verbose_name="元数据", default=dict)
Expand Down Expand Up @@ -65,10 +72,12 @@ class VoteReasonChoices(models.TextChoices):
INCOMPLETE = 'incomplete', '内容不完善'
OTHER = 'other', '其他'


class ShareLinkType(models.TextChoices):
PUBLIC = "PUBLIC", 'public'
PRIVATE = "PRIVATE", 'private'


class ChatSourceChoices(models.TextChoices):
ONLINE = "ONLINE", "线上使用"
API_CALL = "API_CALL", "API调用"
Expand Down Expand Up @@ -144,10 +153,11 @@ class Meta:
models.Index(fields=['application_id', 'chat_user_id']),
]


class ChatShareLink(AppModelMixin):
id = models.UUIDField(primary_key=True, max_length=128, default=uuid.uuid7, editable=False, verbose_name="主键id")
chat = models.ForeignKey(Chat, on_delete=models.CASCADE)
application = models.ForeignKey(Application,on_delete=models.CASCADE)
application = models.ForeignKey(Application, on_delete=models.CASCADE)
share_type = models.CharField(max_length=20, choices=ShareLinkType.choices, default=ShareLinkType.PUBLIC)
user = models.ForeignKey(User, on_delete=models.SET_NULL, db_constraint=False, blank=True, null=True)
chat_record_ids = ArrayField(base_field=models.UUIDField(max_length=128))
Expand All @@ -156,11 +166,10 @@ class Meta:
db_table = "application_chat_share_link"



class ApplicationLongTermMemory(AppModelMixin):
id = models.UUIDField(primary_key=True, max_length=128, default=uuid.uuid7, editable=False, verbose_name="主键id")
application = models.ForeignKey(Application, on_delete=models.CASCADE, db_constraint=False, verbose_name="所属应用")
chat_user_id = models.CharField( max_length=128, verbose_name="对话用户id", db_index=True)
chat_user_id = models.CharField(max_length=128, verbose_name="对话用户id", db_index=True)
memory = models.TextField(verbose_name="长期记忆内容", default="")

class Meta:
Expand Down
101 changes: 51 additions & 50 deletions apps/application/serializers/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
from django.utils import timezone
from django.utils.translation import gettext_lazy as _

from application.models import Application, ChatRecord, Chat, ApplicationVersion, ChatUserType, ApplicationTypeChoices
from application.models import Application, ChatRecord, Chat, ApplicationVersion, ChatUserType, ApplicationTypeChoices, \
ExecuteType
from application.serializers.application_chat import ChatCountSerializer
from common.constants.cache_version import Cache_Version
from common.database_model_manage.database_model_manage import DatabaseModelManage
Expand Down Expand Up @@ -280,6 +281,13 @@ def set_chat(self, question):
ip_address=self.ip_address, source=self.source,
asker=self.get_chat_user()).save()

def save_chat(self):
Chat(id=self.chat_id, application_id=self.application_id, abstract="新建对话",
execute_type=ExecuteType.DEBUG if self.debug else ExecuteType.CHAT,
chat_user_id=self.chat_user_id, chat_user_type=self.chat_user_type,
ip_address=self.ip_address, source=self.source,
asker=self.get_chat_user()).save()

def set_chat_variable(self, chat_context):
if not self.debug:
chat = QuerySet(Chat).filter(id=self.chat_id).first()
Expand Down Expand Up @@ -314,55 +322,48 @@ def append_chat_record(self, chat_record: ChatRecord):
break
if is_save:
self.chat_record_list.append(chat_record)

# 调试模式:把 workflow_context 存到 Redis
if self.debug and chat_record.workflow_context:
from django.core.cache import cache
from common.constants.cache_version import Cache_Version
cache_key = Cache_Version.DEBUG_WORKFLOW_CONTEXT.get_key(chat_record_id=str(chat_record.id))
cache.set(cache_key, chat_record.workflow_context, timeout=3600)

if not self.debug:
if not QuerySet(Chat).filter(id=self.chat_id).exists():
Chat(id=self.chat_id, application_id=self.application_id, abstract=chat_record.problem_text[0:1024],
chat_user_id=self.chat_user_id, chat_user_type=self.chat_user_type,
ip_address=self.ip_address, source=self.source,
asker=self.get_chat_user()).save()
else:
QuerySet(Chat).filter(id=self.chat_id).update(update_time=timezone.now())
# 插入会话记录
QuerySet(ChatRecord).update_or_create(id=chat_record.id,
create_defaults={'id': chat_record.id,
'chat_id': chat_record.chat_id,
"vote_status": chat_record.vote_status,
'problem_text': chat_record.problem_text,
'answer_text': chat_record.answer_text,
'answer_text_list': chat_record.answer_text_list,
'message_tokens': chat_record.message_tokens,
'answer_tokens': chat_record.answer_tokens,
'const': chat_record.const,
'details': chat_record.details,
'improve_paragraph_id_list': chat_record.improve_paragraph_id_list,
'run_time': chat_record.run_time,
'source': chat_record.source,
'ip_address': chat_record.ip_address or '',
'index': chat_record.index},
defaults={
"vote_status": chat_record.vote_status,
'problem_text': chat_record.problem_text,
'answer_text': chat_record.answer_text,
'answer_text_list': chat_record.answer_text_list,
'message_tokens': chat_record.message_tokens,
'answer_tokens': chat_record.answer_tokens,
'const': chat_record.const,
'details': chat_record.details,
'improve_paragraph_id_list': chat_record.improve_paragraph_id_list,
'run_time': chat_record.run_time,
'index': chat_record.index,
'source': chat_record.source,
'ip_address': chat_record.ip_address or '',
})
ChatCountSerializer(data={'chat_id': self.chat_id}).update_chat()

if not QuerySet(Chat).filter(id=self.chat_id).exists():
Chat(id=self.chat_id, application_id=self.application_id, abstract=chat_record.problem_text[0:1024],
execute_type=ExecuteType.DEBUG if self.debug else ExecuteType.CHAT,
chat_user_id=self.chat_user_id, chat_user_type=self.chat_user_type,
ip_address=self.ip_address, source=self.source,
asker=self.get_chat_user()).save()
else:
QuerySet(Chat).filter(id=self.chat_id).update(update_time=timezone.now())
# 插入会话记录
QuerySet(ChatRecord).update_or_create(id=chat_record.id,
create_defaults={'id': chat_record.id,
'chat_id': chat_record.chat_id,
"vote_status": chat_record.vote_status,
'problem_text': chat_record.problem_text,
'answer_text': chat_record.answer_text,
'answer_text_list': chat_record.answer_text_list,
'message_tokens': chat_record.message_tokens,
'answer_tokens': chat_record.answer_tokens,
'const': chat_record.const,
'details': chat_record.details,
'improve_paragraph_id_list': chat_record.improve_paragraph_id_list,
'run_time': chat_record.run_time,
'source': chat_record.source,
'ip_address': chat_record.ip_address or '',
'index': chat_record.index},
defaults={
"vote_status": chat_record.vote_status,
'problem_text': chat_record.problem_text,
'answer_text': chat_record.answer_text,
'answer_text_list': chat_record.answer_text_list,
'message_tokens': chat_record.message_tokens,
'answer_tokens': chat_record.answer_tokens,
'const': chat_record.const,
'details': chat_record.details,
'improve_paragraph_id_list': chat_record.improve_paragraph_id_list,
'run_time': chat_record.run_time,
'index': chat_record.index,
'source': chat_record.source,
'ip_address': chat_record.ip_address or '',
})
ChatCountSerializer(data={'chat_id': self.chat_id}).update_chat()

def to_dict(self):

Expand Down
3 changes: 3 additions & 0 deletions apps/application/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,7 @@
path('workspace/<str:workspace_id>/application/<str:application_id>/mcp_tools', views.McpServers.as_view()),
path('workspace/<str:workspace_id>/application/<str:application_id>/model/<str:model_id>/prompt_generate', views.PromptGenerateView.as_view()),
path('chat_message/<str:chat_id>', views.ChatView.as_view()),
path('workspace/<str:workspace_id>/application/<str:application_id>/historical_conversation/<int:current_page>/<int:page_size>', views.DebugHistoricalConversation.PageView.as_view()),
path('workspace/<str:workspace_id>/application/<str:application_id>/historical_conversation/<str:chat_id>', views.DebugHistoricalConversation.Operate.as_view()),
path('workspace/<str:workspace_id>/application/<str:application_id>/historical_conversation_record/<str:chat_id>/<int:current_page>/<int:page_size>', views.DebugHistoricalConversation.RecordPageView.as_view()),
]
84 changes: 80 additions & 4 deletions apps/application/views/application_chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,19 @@

from application.api.application_chat import ApplicationChatQueryAPI, ApplicationChatQueryPageAPI, \
ApplicationChatExportAPI
from application.models import ChatUserType, Application
from application.models import ChatUserType, Application, ChatSourceChoices
from application.serializers.application_chat import ApplicationChatQuerySerializers
from chat.api.chat_api import ChatAPI, PromptGenerateAPI
from chat.api.chat_authentication_api import ChatOpenAPI
from chat.serializers.chat import OpenChatSerializers, ChatSerializers, DebugChatSerializers, PromptGenerateSerializer
from common.auth import TokenAuth
from common.auth.authentication import has_permissions
from common.constants.permission_constants import PermissionConstants, RoleConstants, ViewPermission, CompareConstants
from common.log.log import log
from common.log.log import log, _get_ip_address
from common.result import result
from common.utils.common import query_params_to_single_dict


def get_application_operation_object(application_id):
application_model = QuerySet(model=Application).filter(id=application_id).first()
if application_model is not None:
Expand All @@ -36,6 +37,7 @@ def get_application_operation_object(application_id):
}
return {}


class ApplicationChat(APIView):
authentication_classes = [TokenAuth]

Expand Down Expand Up @@ -132,9 +134,14 @@ class OpenView(APIView):
CompareConstants.AND),
RoleConstants.WORKSPACE_MANAGE.get_workspace_role())
def get(self, request: Request, workspace_id: str, application_id: str):
ip_address = _get_ip_address(request)
return result.success(OpenChatSerializers(
data={'workspace_id': workspace_id, 'application_id': application_id,
'chat_user_id': str(uuid.uuid7()), 'chat_user_type': ChatUserType.ANONYMOUS_USER,
'ip_address': ip_address,
'source': {
'type': ChatSourceChoices.ONLINE.value}
,
'debug': True}).open())


Expand All @@ -154,6 +161,7 @@ class ChatView(APIView):
def post(self, request: Request, chat_id: str):
return DebugChatSerializers(data={'chat_id': chat_id}).chat(request.data)


class PromptGenerateView(APIView):
authentication_classes = [TokenAuth]

Expand All @@ -175,5 +183,73 @@ class PromptGenerateView(APIView):
RoleConstants.WORKSPACE_MANAGE.get_workspace_role())
@log(menu='Application', operate='Generate prompt',
get_operation_object=lambda r, k: get_application_operation_object(k.get('application_id')))
def post(self, request: Request, workspace_id: str, model_id:str, application_id: str):
return PromptGenerateSerializer(data={'workspace_id': workspace_id, 'model_id': model_id, 'application_id': application_id}).generate_prompt(instance=request.data)
def post(self, request: Request, workspace_id: str, model_id: str, application_id: str):
return PromptGenerateSerializer(data={'workspace_id': workspace_id, 'model_id': model_id,
'application_id': application_id}).generate_prompt(instance=request.data)


class DebugHistoricalConversation(APIView):
authentication_classes = [TokenAuth]

class PageView(APIView):
authentication_classes = [TokenAuth]

def get(self, request: Request, workspace_id: str, application_id: str, current_page: int, page_size: int):
from chat.serializers.chat_record import HistoricalConversationSerializer, page_search, HistoryChatModel
from django.db.models import QuerySet
from application.models import Chat

queryset = QuerySet(Chat).filter(
application_id=application_id,
is_deleted=False
).order_by('-update_time', 'id')

return result.success(
page_search(current_page, page_size, queryset, lambda r: HistoryChatModel(r).data)
)

class RecordPageView(APIView):
authentication_classes = [TokenAuth]

def get(self, request: Request, workspace_id: str, application_id: str, chat_id: str, current_page: int,
page_size: int):
from chat.serializers.chat_record import HistoricalConversationRecordSerializer

serializer = HistoricalConversationRecordSerializer(
data={
'application_id': application_id,
'chat_id': chat_id,
'chat_user_id': str(uuid.uuid7()),
}
)
try:
return result.success(serializer.page(current_page, page_size))
except Exception:
from django.db.models import QuerySet
from application.models import ChatRecord
from common.db.search import page_search
from application.serializers.application_chat_record import ChatRecordSerializerModel

queryset = QuerySet(ChatRecord).filter(
chat_id=chat_id,
).order_by('create_time', 'id')

return result.success(
page_search(current_page, page_size, queryset, lambda r: ChatRecordSerializerModel(r).data)
)

class Operate(APIView):
authentication_classes = [TokenAuth]

def delete(self, request: Request, workspace_id: str, application_id: str, chat_id: str):
from django.db.models import QuerySet
from application.models import Chat
QuerySet(Chat).filter(id=chat_id, application_id=application_id).update(is_deleted=True)
return result.success(True)

def put(self, request: Request, workspace_id: str, application_id: str, chat_id: str):
from django.db.models import QuerySet
from application.models import Chat
abstract = request.data.get('abstract', '')
QuerySet(Chat).filter(id=chat_id, application_id=application_id).update(abstract=abstract)
return result.success(True)
Loading
Loading