Skip to content

Commit 6effb8f

Browse files
authored
feat: Application chat (#3494)
1 parent 50e93e4 commit 6effb8f

File tree

20 files changed

+336
-180
lines changed

20 files changed

+336
-180
lines changed

apps/application/chat_pipeline/step/chat_step/impl/base_chat_step.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -307,10 +307,9 @@ def execute_block(self, message_list: List[BaseMessage],
307307
else:
308308
reasoning_content = reasoning_result.get('reasoning_content') + reasoning_result_end.get(
309309
'reasoning_content')
310-
asker = manage.context.get('form_data', {}).get('asker', None)
311310
post_response_handler.handler(chat_id, chat_record_id, paragraph_list, problem_text,
312-
chat_id, manage, self, padding_problem_text,
313-
reasoning_content=reasoning_content if reasoning_content_enable else '')
311+
content, manage, self, padding_problem_text,
312+
reasoning_content=reasoning_content)
314313
add_access_num(client_id, client_type, manage.context.get('application_id'))
315314
return manage.get_base_to_response().to_block_response(str(chat_id), str(chat_record_id),
316315
content, True,
@@ -324,10 +323,8 @@ def execute_block(self, message_list: List[BaseMessage],
324323
except Exception as e:
325324
all_text = 'Exception:' + str(e)
326325
write_context(self, manage, 0, 0, all_text)
327-
asker = manage.context.get('form_data', {}).get('asker', None)
328326
post_response_handler.handler(chat_id, chat_record_id, paragraph_list, problem_text,
329-
chat_id, manage, self, padding_problem_text,
330-
reasoning_content='')
327+
all_text, manage, self, padding_problem_text, reasoning_content='')
331328
add_access_num(client_id, client_type, manage.context.get('application_id'))
332329
return manage.get_base_to_response().to_block_response(str(chat_id), str(chat_record_id), all_text, True, 0,
333330
0, _status=status.HTTP_500_INTERNAL_SERVER_ERROR)

apps/application/flow/step_node/image_understand_step_node/impl/base_image_understand_node.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def file_id_to_base64(file_id: str):
6161
file = QuerySet(File).filter(id=file_id).first()
6262
file_bytes = file.get_bytes()
6363
base64_image = base64.b64encode(file_bytes).decode("utf-8")
64-
return [base64_image, what(None, file_bytes.tobytes())]
64+
return [base64_image, what(None, file_bytes)]
6565

6666

6767
class BaseImageUnderstandNode(IImageUnderstandNode):
@@ -173,7 +173,7 @@ def generate_message_list(self, image_model, system: str, prompt: str, history_m
173173
file = QuerySet(File).filter(id=file_id).first()
174174
image_bytes = file.get_bytes()
175175
base64_image = base64.b64encode(image_bytes).decode("utf-8")
176-
image_format = what(None, image_bytes.tobytes())
176+
image_format = what(None, image_bytes)
177177
images.append(
178178
{'type': 'image_url', 'image_url': {'url': f'data:image/{image_format};base64,{base64_image}'}})
179179
messages = [HumanMessage(

apps/application/flow/step_node/start_node/impl/base_start_node.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,16 @@ def get_default_global_variable(input_field_list: List):
2222

2323

2424
def get_global_variable(node):
25+
body = node.workflow_manage.get_body()
2526
history_chat_record = node.flow_params_serializer.data.get('history_chat_record', [])
2627
history_context = [{'question': chat_record.problem_text, 'answer': chat_record.answer_text} for chat_record in
2728
history_chat_record]
2829
chat_id = node.flow_params_serializer.data.get('chat_id')
2930
return {'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'start_time': time.time(),
30-
'history_context': history_context, 'chat_id': str(chat_id), **node.workflow_manage.form_data}
31+
'history_context': history_context, 'chat_id': str(chat_id), **node.workflow_manage.form_data,
32+
'chat_user_id': body.get('chat_user_id'),
33+
'chat_user_type': body.get('chat_user_type'),
34+
'chat_user': body.get('chat_user')}
3135

3236

3337
class BaseStartStepNode(IStarNode):
@@ -64,6 +68,7 @@ def execute(self, question, **kwargs) -> NodeResult:
6468
'document': self.workflow_manage.document_list,
6569
'audio': self.workflow_manage.audio_list,
6670
'other': self.workflow_manage.other_list,
71+
6772
}
6873
return NodeResult(node_variable, workflow_variable)
6974

apps/application/flow/workflow_manage.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,8 @@ def init_fields(self):
154154
if global_fields is not None:
155155
for global_field in global_fields:
156156
global_field_list.append({**global_field, 'node_id': node_id, 'node_name': node_name})
157-
field_list.sort(key=lambda f: len(f.get('node_name')), reverse=True)
158-
global_field_list.sort(key=lambda f: len(f.get('node_name')), reverse=True)
157+
field_list.sort(key=lambda f: len(f.get('node_name') + f.get('value')), reverse=True)
158+
global_field_list.sort(key=lambda f: len(f.get('node_name') + f.get('value')), reverse=True)
159159
self.field_list = field_list
160160
self.global_field_list = global_field_list
161161

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Generated by Django 5.2.3 on 2025-07-07 02:59
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('application', '0004_application_publish_time_applicationversion_and_more'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='chat',
15+
name='chat_record_count',
16+
field=models.IntegerField(default=0, verbose_name='对话次数'),
17+
),
18+
migrations.AddField(
19+
model_name='chat',
20+
name='mark_sum',
21+
field=models.IntegerField(default=0, verbose_name='标记数量'),
22+
),
23+
migrations.AddField(
24+
model_name='chat',
25+
name='star_num',
26+
field=models.IntegerField(default=0, verbose_name='点赞数量'),
27+
),
28+
migrations.AddField(
29+
model_name='chat',
30+
name='trample_num',
31+
field=models.IntegerField(default=0, verbose_name='点踩数量'),
32+
),
33+
]

apps/application/models/application_chat.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ class Chat(AppModelMixin):
3939
is_deleted = models.BooleanField(verbose_name="逻辑删除", default=False)
4040
asker = models.JSONField(verbose_name="访问者", default=default_asker, encoder=SystemEncoder)
4141
meta = models.JSONField(verbose_name="元数据", default=dict)
42+
star_num = models.IntegerField(verbose_name="点赞数量", default=0)
43+
trample_num = models.IntegerField(verbose_name="点踩数量", default=0)
44+
chat_record_count = models.IntegerField(verbose_name="对话次数", default=0)
45+
mark_sum = models.IntegerField(verbose_name="标记数量", default=0)
4246

4347
class Meta:
4448
db_table = "application_chat"

apps/application/serializers/application_chat.py

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@
2222
from openpyxl.cell.cell import ILLEGAL_CHARACTERS_RE
2323
from rest_framework import serializers
2424

25-
from application.models import Chat, Application
25+
from application.models import Chat, Application, ChatRecord
2626
from common.db.search import get_dynamics_model, native_search, native_page_search
2727
from common.exception.app_exception import AppApiException
2828
from common.utils.common import get_file_content
2929
from maxkb.conf import PROJECT_DIR
30-
from maxkb.settings import TIME_ZONE
30+
from maxkb.settings import TIME_ZONE, edition
3131

3232

3333
class ApplicationChatResponseSerializers(serializers.Serializer):
@@ -120,20 +120,17 @@ def get_query_set(self, select_ids=None):
120120
condition = base_condition & min_trample_query
121121
else:
122122
condition = base_condition
123-
inner_queryset = QuerySet(Chat).filter(application_id=self.data.get("application_id"))
124-
if 'abstract' in self.data and self.data.get('abstract') is not None:
125-
inner_queryset = inner_queryset.filter(abstract__icontains=self.data.get('abstract'))
126123

127124
return {
128-
'inner_queryset': inner_queryset,
129125
'default_queryset': query_set.filter(condition).order_by("-application_chat.update_time")
130126
}
131127

132128
def list(self, with_valid=True):
133129
if with_valid:
134130
self.is_valid(raise_exception=True)
135131
return native_search(self.get_query_set(), select_string=get_file_content(
136-
os.path.join(PROJECT_DIR, "apps", "application", 'sql', 'list_application_chat.sql')),
132+
os.path.join(PROJECT_DIR, "apps", "application", 'sql', ('list_application_chat_ee.sql' if ['PE', 'EE'].__contains__(
133+
edition) else 'list_application_chat.sql'))),
137134
with_table_name=False)
138135

139136
@staticmethod
@@ -144,7 +141,7 @@ def paragraph_list_to_string(paragraph_list):
144141

145142
@staticmethod
146143
def to_row(row: Dict):
147-
details = row.get('details')
144+
details = row.get('details') or {}
148145
padding_problem_text = ' '.join(node.get("answer", "") for key, node in details.items() if
149146
node.get("type") == 'question-node')
150147
search_dataset_node_list = [(key, node) for key, node in details.items() if
@@ -161,26 +158,28 @@ def to_row(row: Dict):
161158
'name') + ':\n' + ApplicationChatQuerySerializers.paragraph_list_to_string(node.get('paragraph_list',
162159
[])) for
163160
key, node in search_dataset_node_list])
164-
improve_paragraph_list = row.get('improve_paragraph_list')
161+
improve_paragraph_list = row.get('improve_paragraph_list') or []
165162
vote_status_map = {'-1': '未投票', '0': '赞同', '1': '反对'}
166163
return [str(row.get('chat_id')), row.get('abstract'), row.get('problem_text'), padding_problem_text,
167164
row.get('answer_text'), vote_status_map.get(row.get('vote_status')), reference_paragraph_len,
168165
reference_paragraph,
169166
"\n".join([
170167
f"{improve_paragraph_list[index].get('title')}\n{improve_paragraph_list[index].get('content')}"
171168
for index in range(len(improve_paragraph_list))]),
172-
row.get('message_tokens') + row.get('answer_tokens'), row.get('run_time'),
169+
(row.get('message_tokens') or 0) + (row.get('answer_tokens') or 0), row.get('run_time'),
173170
str(row.get('create_time').astimezone(pytz.timezone(TIME_ZONE)).strftime('%Y-%m-%d %H:%M:%S')
174-
)]
171+
if row.get('create_time') is not None else None)]
175172

176173
def export(self, data, with_valid=True):
177174
if with_valid:
178175
self.is_valid(raise_exception=True)
179176
ApplicationChatRecordExportRequest(data=data).is_valid(raise_exception=True)
177+
180178
data_list = native_search(self.get_query_set(data.get('select_ids')),
181179
select_string=get_file_content(
182180
os.path.join(PROJECT_DIR, "apps", "application", 'sql',
183-
'export_application_chat.sql')),
181+
('export_application_chat_ee.sql' if ['PE', 'EE'].__contains__(
182+
edition) else 'export_application_chat.sql'))),
184183
with_table_name=False)
185184

186185
batch_size = 500
@@ -232,5 +231,26 @@ def page(self, current_page: int, page_size: int, with_valid=True):
232231
if with_valid:
233232
self.is_valid(raise_exception=True)
234233
return native_page_search(current_page, page_size, self.get_query_set(), select_string=get_file_content(
235-
os.path.join(PROJECT_DIR, "apps", "application", 'sql', 'list_application_chat.sql')),
234+
os.path.join(PROJECT_DIR, "apps", "application", 'sql',
235+
('list_application_chat_ee.sql' if ['PE', 'EE'].__contains__(
236+
edition) else 'list_application_chat.sql'))),
236237
with_table_name=False)
238+
239+
240+
class ChatCountSerializer(serializers.Serializer):
241+
chat_id = serializers.UUIDField(required=True, label=_("Conversation ID"))
242+
243+
def get_query_set(self):
244+
return QuerySet(ChatRecord).filter(chat_id=self.data.get('chat_id'))
245+
246+
def update_chat(self):
247+
self.is_valid(raise_exception=True)
248+
count_chat_record = native_search(self.get_query_set(), get_file_content(
249+
os.path.join(PROJECT_DIR, "apps", "application", 'sql', 'count_chat_record.sql')), with_search_one=True)
250+
QuerySet(Chat).filter(id=self.data.get('chat_id')).update(star_num=count_chat_record.get('star_num', 0) or 0,
251+
trample_num=count_chat_record.get('trample_num',
252+
0) or 0,
253+
chat_record_count=count_chat_record.get(
254+
'chat_record_count', 0) or 0,
255+
mark_sum=count_chat_record.get('mark_sum', 0) or 0)
256+
return True

apps/application/serializers/application_chat_record.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from rest_framework.utils.formatting import lazy_format
1919

2020
from application.models import ChatRecord, ApplicationAccessToken, Application
21+
from application.serializers.application_chat import ChatCountSerializer
2122
from application.serializers.common import ChatInfo
2223
from common.db.search import page_search
2324
from common.exception.app_exception import AppApiException
@@ -359,6 +360,7 @@ def improve(self, instance: Dict, with_valid=True):
359360
update_document_char_length(document_id)
360361
# 添加标注
361362
chat_record.save()
363+
ChatCountSerializer(data={'chat_id': chat_id}).update_chat()
362364
return ChatRecordSerializerModel(chat_record).data, paragraph.id, knowledge_id
363365

364366
class Operate(serializers.Serializer):

apps/application/serializers/common.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@
1414
from django.utils.translation import gettext_lazy as _
1515

1616
from application.chat_pipeline.step.chat_step.i_chat_step import PostResponseHandler
17-
from application.models import Application, ChatRecord, Chat, ApplicationVersion
17+
from application.models import Application, ChatRecord, Chat, ApplicationVersion, ChatUserType
1818
from common.constants.cache_version import Cache_Version
19+
from common.database_model_manage.database_model_manage import DatabaseModelManage
1920
from common.exception.app_exception import ChatException
2021
from models_provider.models import Model
2122
from models_provider.tools import get_model_credential
@@ -47,6 +48,7 @@ def __init__(self,
4748
self.application_id = application_id
4849
self.chat_record_list: List[ChatRecord] = []
4950
self.application = None
51+
self.chat_user = None
5052
self.debug = debug
5153

5254
@staticmethod
@@ -73,8 +75,30 @@ def get_application(self):
7375
self.application = application
7476
return application
7577

78+
def get_chat_user(self, asker=None):
79+
if self.chat_user:
80+
return self.chat_user
81+
if self.chat_user_type == ChatUserType.CHAT_USER.value:
82+
chat_user_model = DatabaseModelManage.get_model("chat_user")
83+
chat_user = QuerySet(chat_user_model).filter(id=self.chat_user_id).first()
84+
return {
85+
'id': chat_user.id,
86+
'email': chat_user.email,
87+
'phone': chat_user.phone,
88+
'nick_name': chat_user.nick_name,
89+
'username': chat_user.username,
90+
'source': chat_user.source
91+
}
92+
else:
93+
if asker:
94+
self.chat_user = asker
95+
else:
96+
self.chat_user = {'username': '游客'}
97+
return self.chat_user
98+
7699
def to_base_pipeline_manage_params(self):
77100
self.get_application()
101+
self.get_chat_user()
78102
knowledge_setting = self.application.knowledge_setting
79103
model_setting = self.application.model_setting
80104
model_id = self.application.model_id
@@ -139,7 +163,8 @@ def append_chat_record(self, chat_record: ChatRecord):
139163
if not self.debug:
140164
if not QuerySet(Chat).filter(id=self.chat_id).exists():
141165
Chat(id=self.chat_id, application_id=self.application_id, abstract=chat_record.problem_text[0:1024],
142-
chat_user_id=self.chat_user_id, chat_user_type=self.chat_user_type).save()
166+
chat_user_id=self.chat_user_id, chat_user_type=self.chat_user_type,
167+
asker=self.get_chat_user()).save()
143168
else:
144169
QuerySet(Chat).filter(id=self.chat_id).update(update_time=datetime.now())
145170
# 插入会话记录
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
SELECT COUNT
2+
( "id" ) AS chat_record_count,
3+
SUM ( CASE WHEN "vote_status" = '0' THEN 1 ELSE 0 END ) AS star_num,
4+
SUM ( CASE WHEN "vote_status" = '1' THEN 1 ELSE 0 END ) AS trample_num,
5+
SUM ( CASE WHEN array_length( application_chat_record.improve_paragraph_id_list, 1 ) IS NULL THEN 0 ELSE array_length( application_chat_record.improve_paragraph_id_list, 1 ) END ) AS mark_sum
6+
FROM
7+
application_chat_record

0 commit comments

Comments
 (0)