Skip to content

Commit 7490871

Browse files
committed
feat: Support reasoning content(WIP)
1 parent ba286ec commit 7490871

File tree

5 files changed

+91
-7
lines changed

5 files changed

+91
-7
lines changed

apps/application/flow/i_step_node.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ def get_answer_list(self) -> List[Answer] | None:
161161
return None
162162
return [
163163
Answer(self.answer_text, self.view_type, self.runtime_node_id, self.workflow_params['chat_record_id'], {},
164-
self.runtime_node_id, self.context.get('reasoning_content'))]
164+
self.runtime_node_id, self.context.get('reasoning_content', ''))]
165165

166166
def __init__(self, node, workflow_params, workflow_manage, up_node_id_list=None,
167167
get_node_params=lambda node: node.properties.get('node_data')):

apps/application/flow/step_node/ai_chat_step_node/i_chat_node.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ class ChatNodeSerializer(serializers.Serializer):
2828
error_messages=ErrMessage.boolean(_('Whether to return content')))
2929

3030
model_params_setting = serializers.DictField(required=False,
31-
error_messages=ErrMessage.integer(_("Model parameter settings")))
32-
31+
error_messages=ErrMessage.dict(_("Model parameter settings")))
32+
model_setting = serializers.DictField(required=False,
33+
error_messages=ErrMessage.dict('Model settings'))
3334
dialogue_type = serializers.CharField(required=False, allow_blank=True, allow_null=True,
3435
error_messages=ErrMessage.char(_("Context Type")))
3536

@@ -47,5 +48,6 @@ def execute(self, model_id, system, prompt, dialogue_number, history_chat_record
4748
chat_record_id,
4849
model_params_setting=None,
4950
dialogue_type=None,
51+
model_setting=None,
5052
**kwargs) -> NodeResult:
5153
pass

apps/application/flow/step_node/ai_chat_step_node/impl/base_chat_node.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from application.flow.common import Answer
1818
from application.flow.i_step_node import NodeResult, INode
1919
from application.flow.step_node.ai_chat_step_node.i_chat_node import IChatNode
20+
from application.flow.tools import Reasoning
2021
from setting.models import Model
2122
from setting.models_provider import get_model_credential
2223
from setting.models_provider.tools import get_model_instance_by_model_user_id
@@ -49,13 +50,24 @@ def write_context_stream(node_variable: Dict, workflow_variable: Dict, node: INo
4950
response = node_variable.get('result')
5051
answer = ''
5152
reasoning_content = ''
53+
model_setting = node.context.get('model_setting',
54+
{'reasoning_content_enable': False, 'reasoning_content_end': '</think>',
55+
'reasoning_content_start': '<think>'})
56+
reasoning = Reasoning(model_setting.get('reasoning_content_start'), model_setting.get('reasoning_content_end'))
5257
for chunk in response:
53-
answer += chunk.content
54-
reasoning_content_chunk = chunk.additional_kwargs.get('reasoning_content', '')
58+
reasoning_chunk = reasoning.get_reasoning_content(chunk)
59+
content_chunk = reasoning_chunk.get('content')
60+
if 'reasoning_content' in chunk.additional_kwargs:
61+
reasoning_content_chunk = chunk.additional_kwargs.get('reasoning_content', '')
62+
else:
63+
reasoning_content_chunk = reasoning_chunk.get('reasoning_content')
64+
answer += content_chunk
5565
if reasoning_content_chunk is None:
5666
reasoning_content_chunk = ''
5767
reasoning_content += reasoning_content_chunk
58-
yield {'content': chunk.content, 'reasoning_content': reasoning_content_chunk}
68+
yield {'content': content_chunk,
69+
'reasoning_content': reasoning_content_chunk if model_setting.get('reasoning_content_enable',
70+
False) else ''}
5971
_write_context(node_variable, workflow_variable, node, workflow, answer, reasoning_content)
6072

6173

@@ -107,12 +119,17 @@ def save_context(self, details, workflow_manage):
107119
def execute(self, model_id, system, prompt, dialogue_number, history_chat_record, stream, chat_id, chat_record_id,
108120
model_params_setting=None,
109121
dialogue_type=None,
122+
model_setting=None,
110123
**kwargs) -> NodeResult:
111124
if dialogue_type is None:
112125
dialogue_type = 'WORKFLOW'
113126

114127
if model_params_setting is None:
115128
model_params_setting = get_default_model_params_setting(model_id)
129+
if model_setting is None:
130+
model_setting = {'reasoning_content_enable': False, 'reasoning_content_end': '</think>',
131+
'reasoning_content_start': '<think>'}
132+
self.context['model_setting'] = model_setting
116133
chat_model = get_model_instance_by_model_user_id(model_id, self.flow_params_serializer.data.get('user_id'),
117134
**model_params_setting)
118135
history_message = self.get_history_message(history_chat_record, dialogue_number, dialogue_type,

apps/application/flow/step_node/application_node/impl/base_application_node.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,8 @@ def get_answer_list(self) -> List[Answer] | None:
153153
return [Answer(n.get('content'), n.get('view_type'), self.runtime_node_id,
154154
self.workflow_params['chat_record_id'], {'runtime_node_id': n.get('runtime_node_id'),
155155
'chat_record_id': n.get('chat_record_id')
156-
, 'child_node': n.get('child_node')}, n.get('real_node_id'), n.get('reasoning_content'))
156+
, 'child_node': n.get('child_node')}, n.get('real_node_id'),
157+
n.get('reasoning_content', ''))
157158
for n in
158159
sorted(application_node_dict.values(), key=lambda item: item.get('index'))]
159160

apps/application/flow/tools.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,70 @@
1616
from common.response import result
1717

1818

19+
class Reasoning:
20+
def __init__(self, reasoning_content_start, reasoning_content_end):
21+
self.content = ""
22+
self.reasoning_content = ""
23+
self.all_content = ""
24+
self.reasoning_content_start_tag = reasoning_content_start
25+
self.reasoning_content_end_tag = reasoning_content_end
26+
self.reasoning_content_start_tag_len = len(reasoning_content_start)
27+
self.reasoning_content_end_tag_len = len(reasoning_content_end)
28+
self.reasoning_content_end_tag_prefix = reasoning_content_end[0]
29+
self.reasoning_content_is_start = False
30+
self.reasoning_content_is_end = False
31+
self.reasoning_content_chunk = ""
32+
33+
def get_reasoning_content(self, chunk):
34+
self.all_content += chunk.content
35+
if not self.reasoning_content_is_start and len(self.all_content) >= self.reasoning_content_start_tag_len:
36+
if self.all_content.startswith(self.reasoning_content_start_tag):
37+
self.reasoning_content_is_start = True
38+
self.reasoning_content_chunk = self.all_content[self.reasoning_content_start_tag_len:]
39+
else:
40+
self.reasoning_content_is_end = True
41+
else:
42+
if self.reasoning_content_is_start:
43+
self.reasoning_content_chunk += chunk.content
44+
reasoning_content_end_tag_prefix_index = self.reasoning_content_chunk.find(
45+
self.reasoning_content_end_tag_prefix)
46+
if self.reasoning_content_is_end:
47+
self.content += chunk.content
48+
return {'content': chunk.content, 'reasoning_content': ''}
49+
# 是否包含结束
50+
if reasoning_content_end_tag_prefix_index > -1:
51+
if len(
52+
self.reasoning_content_chunk) - reasoning_content_end_tag_prefix_index > self.reasoning_content_end_tag_len:
53+
reasoning_content_end_tag_index = self.reasoning_content_chunk.find(self.reasoning_content_end_tag)
54+
if reasoning_content_end_tag_index > -1:
55+
reasoning_content_chunk = self.reasoning_content_chunk[0:reasoning_content_end_tag_index]
56+
content_chunk = self.reasoning_content_chunk[
57+
reasoning_content_end_tag_index + self.reasoning_content_end_tag_len:]
58+
self.reasoning_content += reasoning_content_chunk
59+
self.content += content_chunk
60+
self.reasoning_content_chunk = ""
61+
self.reasoning_content_is_end = True
62+
return {'content': content_chunk, 'reasoning_content': reasoning_content_chunk}
63+
else:
64+
reasoning_content_chunk = self.reasoning_content_chunk[0:reasoning_content_end_tag_prefix_index + 1]
65+
self.reasoning_content_chunk = self.reasoning_content_chunk.replace(reasoning_content_chunk, '')
66+
self.reasoning_content += reasoning_content_chunk
67+
return {'content': '', 'reasoning_content': reasoning_content_chunk}
68+
else:
69+
return {'content': '', 'reasoning_content': ''}
70+
71+
else:
72+
if self.reasoning_content_is_end:
73+
self.content += chunk.content
74+
return {'content': chunk.content, 'reasoning_content': ''}
75+
else:
76+
# aaa
77+
result = {'content': '', 'reasoning_content': self.reasoning_content_chunk}
78+
self.reasoning_content += self.reasoning_content_chunk
79+
self.reasoning_content_chunk = ""
80+
return result
81+
82+
1983
def event_content(chat_id, chat_record_id, response, workflow,
2084
write_context,
2185
post_handler: WorkFlowPostHandler):

0 commit comments

Comments
 (0)