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/0014_chatrecord_workflow_context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.2.14 on 2026-07-14 08:49

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('application', '0013_application_long_term_enable_and_more'),
]

operations = [
migrations.AddField(
model_name='chatrecord',
name='workflow_context',
field=models.JSONField(blank=True, default=dict, null=True, verbose_name='工作流上下文'),
),
]
1 change: 1 addition & 0 deletions apps/application/models/application_chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ class ChatRecord(AppModelMixin):
index = models.IntegerField(verbose_name="对话下标")
source = models.JSONField(verbose_name="来源", default=dict)
ip_address = models.CharField(max_length=128, verbose_name="ip地址", default='')
workflow_context = models.JSONField(verbose_name="工作流上下文", default=dict, null=True, blank=True)

def get_human_message(self):
if 'problem_padding' in self.details:
Expand Down
8 changes: 8 additions & 0 deletions apps/application/serializers/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,14 @@ 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],
Expand Down
16 changes: 16 additions & 0 deletions apps/application/workflow/loop_workflow_manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,23 @@ def get_parent_context(self, node_id, key):

def generate_prompt(self, prompt):
prompt = self.workflow.reset_prompt(prompt)
prompt = self.parent_workflow_manage.workflow.reset_prompt(prompt)
context = {**self.context, **self.parent_workflow_manage.context}
from langchain_core.prompts import PromptTemplate
prompt_template = PromptTemplate.from_template(prompt, template_format='jinja2')
return prompt_template.format(context=context)

def get_reference_field(self, node_id, fields):
"""
获取引用字段,先从当前工作流获取,获取不到再从父工作流获取
@param node_id: 节点id
@param fields: 字段
@return: 引用数据
"""
# 先从当前工作流获取
result = super().get_reference_field(node_id, fields)
if result is not None:
return result

# 从父工作流获取
return self.parent_workflow_manage.get_reference_field(node_id, fields)
13 changes: 12 additions & 1 deletion apps/application/workflow/message/struct/content.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
@date:2026/6/30 15:38
@desc:
"""
from enum import Enum
from typing import Optional

from application.workflow.content_type import ContentType
from application.workflow.status import Status

Expand All @@ -17,10 +20,18 @@ def __init__(self, _id: str, name: str, status: Status):
self.status = status


class Position:
def __init__(self, _id: str, index: Optional[int] = None, children: Optional['Position'] = None):
self.id = _id
self.index = index
self.children = children


class Content:
def __init__(self, _id, status: Status, _type: ContentType, node_info: NodeInfo, **kwargs):
def __init__(self, _id, status: Status, _type: ContentType, node_info: NodeInfo, position: Position, **kwargs):
self.id = _id
self.status = status
self.type = _type
self.node_info = node_info
self.position = position
self.extra = kwargs
24 changes: 24 additions & 0 deletions apps/application/workflow/message/struct/form_content.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# coding=utf-8
"""
@project: MaxKB
@Author:虎虎虎
@file: form_content.py
@date:2026/7/6 15:30
@desc:
"""
from typing import List, Dict, Optional

from application.workflow.content_type import ContentType
from application.workflow.message.struct.content import Content, NodeInfo, Position
from application.workflow.status import Status


class FormContent(Content):
def __init__(self, _id, form_field_list: List[Dict], form_content_format: str,
is_submit: bool, status: Status, node_info: NodeInfo, position: Position,
form_data: Optional[Dict] = None, **kwargs):
self.form_field_list = form_field_list
self.form_content_format = form_content_format
self.is_submit = is_submit
self.form_data = form_data or {}
super().__init__(_id, status, ContentType.FORM, node_info, position, **kwargs)
6 changes: 3 additions & 3 deletions apps/application/workflow/message/struct/reasoning_content.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
@desc:
"""
from application.workflow.content_type import ContentType
from application.workflow.message.struct.content import Content, NodeInfo
from application.workflow.message.struct.content import Content, NodeInfo, Position
from application.workflow.status import Status


class ReasoningContent(Content):
def __init__(self, _id, content: str, status: Status, node_info: NodeInfo, **kwargs):
def __init__(self, _id, content: str, status: Status, node_info: NodeInfo, position: Position, **kwargs):
self.content = content
super().__init__(_id, status, ContentType.REASONING, node_info, **kwargs)
super().__init__(_id, status, ContentType.REASONING, node_info, position, **kwargs)
6 changes: 3 additions & 3 deletions apps/application/workflow/message/struct/text_content.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
@desc:
"""
from application.workflow.content_type import ContentType
from application.workflow.message.struct.content import Content, NodeInfo
from application.workflow.message.struct.content import Content, NodeInfo, Position
from application.workflow.status import Status


class TextContent(Content):
def __init__(self, _id, content: str, status: Status, node_info: NodeInfo, **kwargs):
def __init__(self, _id, content: str, status: Status, node_info: NodeInfo, position: Position, **kwargs):
self.content = content
super().__init__(_id, status, ContentType.TEXT, node_info, **kwargs)
super().__init__(_id, status, ContentType.TEXT, node_info, position, **kwargs)
7 changes: 4 additions & 3 deletions apps/application/workflow/message/struct/tool_content.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
@desc:
"""
from application.workflow.content_type import ContentType
from application.workflow.message.struct.content import Content, NodeInfo
from application.workflow.message.struct.content import Content, NodeInfo, Position
from application.workflow.status import Status


class ToolContent(Content):
def __init__(self, _id, tool_name: str, arguments: str, result: str, status: Status, node_info: NodeInfo, **kwargs):
def __init__(self, _id, tool_name: str, arguments: str, result: str, status: Status, node_info: NodeInfo,
position: Position, **kwargs):
self.content = tool_name
self.arguments = arguments
self.result = result
super().__init__(_id, status, ContentType.TOOL, node_info, **kwargs)
super().__init__(_id, status, ContentType.TOOL, node_info, position, **kwargs)
14 changes: 13 additions & 1 deletion apps/application/workflow/nodes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,26 @@ def get_node_class(_type, workflow_type):
return node_class


def get_start_node(workflow, workflow_manage, workflow_type):
def get_start_node(workflow, workflow_manage, workflow_type, position=None):
"""
获取开始节点实例
@param workflow: 工作流对象
@param workflow_manage 工作流管理器
@param workflow_type: 工作流类型
@param position: 位置信息(可选)
@return: 开始节点实例
"""
# 如果有 position,根据 position 确定开始节点
if position and position.get('id'):
node_id = position.get('id')
node = workflow.get_node(node_id)
if node:
node_class = get_node_class(node.type, workflow_type)
def get_node_parameters(n):
return n.properties.get('node_data', {})
return node_class(node, workflow_manage, get_node_parameters)

# 默认返回开始节点
start_node = workflow.get_node('start-node')
if start_node is None:
raise ValueError("开始节点不存在")
Expand Down
26 changes: 18 additions & 8 deletions apps/application/workflow/nodes/ai_chat_node/ai_chat_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from application.models import Application, ApplicationAccessToken, ApplicationApiKey
from application.workflow.common import WorkflowType
from application.workflow.i_node import INode
from application.workflow.message.struct.content import NodeInfo
from application.workflow.message.struct.content import NodeInfo, Position
from application.workflow.message.struct.reasoning_content import ReasoningContent
from application.workflow.message.struct.text_content import TextContent
from application.workflow.message.struct.tool_content import ToolContent
Expand Down Expand Up @@ -261,7 +261,7 @@ def execute(self):
if is_result:
answer = self.get_context('answer')
node_info = NodeInfo(self.get_node_id(), self.get_node_name(), Status.SUCCESS)
self.write(TextContent(text_content_id, answer, Status.SUCCESS, node_info))
self.write(TextContent(text_content_id, answer, Status.SUCCESS, node_info, Position(self.get_node_id())))

def _generate_prompt_question(self, prompt, model, vision, image_list, video_list):
images = []
Expand Down Expand Up @@ -301,21 +301,29 @@ def _stream_response(self, response, chat_model, message_list, question,
if reasoning_content_chunk is None:
reasoning_content_chunk = ''
reasoning_content += reasoning_content_chunk

reasoning_end = False
if content_chunk:
self.write(TextContent(text_content_id, content_chunk, Status.RUNNING, node_info))
if not reasoning_end:
self.write(
ReasoningContent(reasoning_content_id, '', Status.SUCCESS, node_info,
Position(self.get_node_id())))
self.write(TextContent(text_content_id, content_chunk, Status.RUNNING, node_info,
Position(self.get_node_id())))
if reasoning_content_chunk and model_setting.get('reasoning_content_enable', False):
self.write(ReasoningContent(reasoning_content_id, reasoning_content_chunk, Status.RUNNING, node_info))
self.write(ReasoningContent(reasoning_content_id, reasoning_content_chunk, Status.RUNNING, node_info,
Position(self.get_node_id())))

reasoning_end = reasoning.get_end_reasoning_content()
answer += reasoning_end.get('content')
reasoning_content_chunk = ''
if not response_reasoning_content:
reasoning_content_chunk = reasoning_end.get('reasoning_content')
if reasoning_end.get('content'):
self.write(TextContent(text_content_id, reasoning_end.get('content'), Status.RUNNING, node_info))
self.write(TextContent(text_content_id, reasoning_end.get('content'), Status.RUNNING, node_info,
Position(self.get_node_id())))
if reasoning_content_chunk and model_setting.get('reasoning_content_enable', False):
self.write(ReasoningContent(reasoning_content_id, reasoning_content_chunk, Status.RUNNING, node_info))
self.write(ReasoningContent(reasoning_content_id, reasoning_content_chunk, Status.RUNNING, node_info,
Position(self.get_node_id())))

self._write_final_context(chat_model, message_list, question, answer, reasoning_content)

Expand Down Expand Up @@ -475,6 +483,7 @@ def _handle_mcp(
chunk.content,
Status.RUNNING,
node_info,
Position(self.get_node_id())
))
continue

Expand All @@ -484,7 +493,8 @@ def _handle_mcp(

answer += chunk.content if hasattr(chunk, 'content') else str(chunk)
if chunk.content:
self.write(TextContent(text_content_id, chunk.content, Status.RUNNING, node_info))
self.write(TextContent(text_content_id, chunk.content, Status.RUNNING, node_info,
Position(self.get_node_id())))
self._write_final_context(chat_model, message_list, question.content, answer, '')
return True

Expand Down
9 changes: 9 additions & 0 deletions apps/application/workflow/nodes/form_node/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# coding=utf-8
"""
@project: MaxKB
@Author:虎虎虎
@file: __init__.py
@date:2026/7/6 15:30
@desc:
"""
from .form_node import FormNode
Loading
Loading