Skip to content

Commit a950d34

Browse files
committed
feat: history message
1 parent 9dc63b6 commit a950d34

4 files changed

Lines changed: 40 additions & 21 deletions

File tree

apps/application/models/application_chat.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,10 @@ def get_ai_message(self):
135135
return to_ai_message_list(self.messages)
136136

137137
def get_node_details_runtime_node_id(self, runtime_node_id):
138-
return self.details.get(runtime_node_id, None)
138+
for node_details in self.details:
139+
if node_details.get("node_id") == runtime_node_id:
140+
return node_details
141+
return None
139142

140143
class Meta:
141144
db_table = "application_chat_record"

apps/application/workflow/nodes/ai_chat_node/ai_chat_node.py

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,20 @@
1818
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage, ToolMessage
1919
from rest_framework import serializers
2020

21+
from application.workflow.message.aggregator import AggregationManager
2122
from application.flow.tools import get_tools, mcp_response_generator
2223
from application.models import Application, ApplicationAccessToken, ApplicationApiKey
2324
from application.workflow.common import WorkflowType
2425
from application.workflow.i_node import INode
25-
from application.workflow.message.struct.content import NodeInfo, Position
26+
from application.workflow.message.struct.content import NodeInfo, Position, Content
2627
from application.workflow.message.struct.reasoning_content import ReasoningContent
2728
from application.workflow.message.struct.text_content import TextContent
2829
from application.workflow.message.struct.tool_content import ToolContent
2930
from application.workflow.status import Status
3031
from application.workflow.tools import Reasoning
3132
from common.exception.app_exception import AppApiException
3233
from common.utils.common import guess_image_format
34+
from common.utils.messages_util import to_ai_message_list, to_human_message_list
3335
from common.utils.rsa_util import rsa_long_decrypt
3436
from common.utils.shared_resource_auth import filter_authorized_ids
3537
from common.utils.tool_code import ToolExecutor
@@ -96,7 +98,8 @@ def _get_node_message(chat_record, runtime_node_id):
9698
node_details = chat_record.get_node_details_runtime_node_id(runtime_node_id)
9799
if node_details is None:
98100
return []
99-
return [HumanMessage(node_details.get("question")), AIMessage(node_details.get("answer"))]
101+
return [*to_human_message_list(node_details.get("question")), *to_ai_message_list(node_details.get("messages"))]
102+
return [HumanMessage(node_details.get("question")), AIMessage(node_details.get("messages"))]
100103

101104

102105
def _get_workflow_message(chat_record):
@@ -193,6 +196,12 @@ class AIChatNode(INode):
193196
supported_workflow_type_list = [WorkflowType.APPLICATION, WorkflowType.KNOWLEDGE, WorkflowType.TOOL]
194197
type = "ai-chat-node"
195198

199+
def write(self, message: Content):
200+
super().write(message)
201+
if not self.data.get("messages"):
202+
self.data["messages"] = []
203+
self.data["messages"].append(message)
204+
196205
def execute(self):
197206
workflow_params = self.get_workflow_parameters()
198207
node_params = self.get_parameters()
@@ -254,7 +263,7 @@ def execute(self):
254263
"reasoning_content_end": "</think>",
255264
"reasoning_content_start": "<think>",
256265
}
257-
self.write_context("model_setting", model_setting)
266+
self.data["model_setting"] = model_setting
258267

259268
chat_model = get_model_instance_by_model_workspace_id(model_id, workspace_id, **(model_params_setting or {}))
260269

@@ -263,12 +272,12 @@ def execute(self):
263272
"history_message",
264273
[{"content": message.content, "role": message.type} for message in (history_message or [])],
265274
)
266-
267-
question = self._generate_prompt_question(prompt, chat_model, vision, image_list, video_list)
268-
self.write_context("question", question.content)
275+
question_str = self.workflow_manage.generate_prompt(prompt)
276+
question = self._generate_prompt_question(question_str, chat_model, vision, image_list, video_list)
277+
self.data["question"] = {"content": question_str, "image_list": image_list, "video_list": video_list}
269278

270279
system = self.workflow_manage.generate_prompt(system)
271-
self.write_context("system", system)
280+
self.data["system"] = system
272281

273282
message_list = [*history_message, question]
274283

@@ -321,7 +330,7 @@ def execute(self):
321330
r, chat_model, message_list_with_system, question.content, is_result, text_content_id
322331
)
323332

324-
def _generate_prompt_question(self, prompt, model, vision, image_list, video_list):
333+
def _generate_prompt_question(self, question_str, model, vision, image_list, video_list):
325334
images = []
326335
videos = []
327336
if vision:
@@ -334,9 +343,7 @@ def _generate_prompt_question(self, prompt, model, vision, image_list, video_lis
334343
if video_list:
335344
video = self.workflow_manage.get_reference_field(video_list[0], video_list[1:])
336345
videos = _process_videos(video, model)
337-
return HumanMessage(
338-
content=[*videos, *images, {"type": "text", "text": self.workflow_manage.generate_prompt(prompt)}]
339-
)
346+
return HumanMessage(content=[*videos, *images, {"type": "text", "text": question_str}])
340347

341348
def _stream_response(self, response, chat_model, message_list, question, reasoning_content_id, text_content_id):
342349
node_info = NodeInfo(self.get_node_id(), self.get_node_name(), Status.RUNNING)
@@ -436,11 +443,10 @@ def _invoke_response(self, response, chat_model, message_list, question, is_resu
436443
def _write_final_context(self, chat_model, message_list, question, answer, reasoning_content):
437444
message_tokens = chat_model.get_num_tokens_from_messages(message_list)
438445
answer_tokens = chat_model.get_num_tokens(answer)
439-
self.write_context("message_tokens", message_tokens)
440-
self.write_context("answer_tokens", answer_tokens)
446+
self.data["message_tokens"] = message_tokens
447+
self.data["answer_tokens"] = answer_tokens
441448
self.write_context("answer", answer)
442-
self.write_context("question", question)
443-
self.write_context("reasoning_content", reasoning_content)
449+
self.data["reasoning_content"] = reasoning_content
444450

445451
def _handle_mcp(
446452
self,
@@ -498,7 +504,6 @@ def _handle_mcp(
498504

499505
tools = get_tools(source_type, chat_id, tool_ids, workspace_id)
500506
if tool_ids and len(tool_ids) > 0:
501-
self.write_context("tool_ids", tool_ids)
502507
custom_tools_map = {
503508
str(t.id): t for t in QuerySet(Tool).filter(id__in=tool_ids, tool_type=ToolType.CUSTOM, is_active=True)
504509
}
@@ -516,7 +521,6 @@ def _handle_mcp(
516521
mcp_servers_config[str(tool.id)] = tool_config
517522

518523
if application_ids and len(application_ids) > 0:
519-
self.write_context("application_ids", application_ids)
520524
apps_map = {str(a.id): a for a in QuerySet(Application).filter(id__in=application_ids, is_publish=True)}
521525
app_keys_map = {
522526
str(ak.application_id): ak
@@ -550,7 +554,6 @@ def _handle_mcp(
550554
mcp_servers_config[app.name] = app_config
551555

552556
if skill_tool_ids and len(skill_tool_ids) > 0:
553-
self.write_context("skill_tool_ids", skill_tool_ids)
554557
skill_file_items = []
555558
skill_tools_map = {str(t.id): t for t in QuerySet(Tool).filter(id__in=skill_tool_ids, is_active=True)}
556559
for tool_id in skill_tool_ids:
@@ -632,14 +635,19 @@ def _get_reference_content(self, fields):
632635

633636
def get_details(self, index: int = 0, position: dict = None, old_details: dict = None, **kwargs):
634637
details = super().get_details(index, position, old_details, **kwargs)
638+
aggregation = AggregationManager()
639+
for m in self.data.get("messages"):
640+
aggregation.aggregate(m)
641+
messages = aggregation.get_contents()
635642
details.update(
636643
{
637-
"question": self.get_context("question"),
644+
"question": self.data.get("question"),
638645
"answer": self.get_context("answer"),
639646
"reasoning_content": self.get_context("reasoning_content"),
640647
"message_tokens": self.get_context("message_tokens"),
641648
"answer_tokens": self.get_context("answer_tokens"),
642649
"history_message": self.get_context("history_message"),
650+
"messages": messages,
643651
}
644652
)
645653
return details

apps/chat/serializers/chat.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,19 +329,25 @@ def on_complete(wf_manage, error):
329329
)
330330
messages = aggregation.get_contents()
331331
old_details = None
332+
chat_record = None
332333
if chat_record_id is not None:
333334
chat_record = QuerySet(ChatRecord).filter(id=chat_record_id).first()
334335
if chat_record:
335336
old_details = chat_record.details
337+
if position and chat_record.messages:
338+
messages = [*chat_record.messages, *messages]
336339
details = wf_manage.get_details(position=position, old_details=old_details)
337340
self.update_chat_record(chat_user_id, chat_record_id_str, wf_manage.context, messages, details)
338341
ChatCountSerializer(data={"chat_id": chat_id}).update_chat()
342+
# 表单续跑时 message_dict.content 为空;保留原记录里的用户问题,避免 WORKFLOW 历史丢问题
343+
question = chat_record.question if (chat_record and chat_record.question) else message_dict
339344
ChatHistory(chat_id).append(
340345
ChatRecord(
341346
id=chat_record_id_str,
342347
chat_id=chat_id,
343-
question=message_dict,
348+
question=question,
344349
messages=messages,
350+
details=details,
345351
create_time=timezone.now(),
346352
)
347353
)

apps/chat/serializers/chat_history.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ def _to_map(r):
4040
"chat_id": str(r.chat_id),
4141
"question": r.question,
4242
"messages": r.messages,
43+
"details": r.details,
4344
"create_time": r.create_time,
4445
}
4546

@@ -50,6 +51,7 @@ def _from_map(d):
5051
chat_id=d.get("chat_id"),
5152
question=d.get("question"),
5253
messages=d.get("messages"),
54+
details=d.get("details"),
5355
create_time=d.get("create_time"),
5456
)
5557

0 commit comments

Comments
 (0)