2024-12-14 17:51:11 +08:00
from __future__ import annotations
import typing
import json
import uuid
2024-12-24 11:26:33 +08:00
import base64
2024-12-14 17:51:11 +08:00
2025-01-06 21:28:36 +08:00
2024-12-14 17:51:11 +08:00
from . . import runner
2025-06-15 22:04:31 +08:00
from . . . core import app
2025-07-13 20:30:17 +08:00
import langbot_plugin . api . entities . builtin . provider . message as provider_message
2024-12-14 17:51:11 +08:00
from . . . utils import image
2025-06-15 22:04:31 +08:00
import langbot_plugin . api . entities . builtin . pipeline . query as pipeline_query
2024-12-14 17:51:11 +08:00
from libs . dify_service_api . v1 import client , errors
2025-11-10 21:42:09 +08:00
2025-04-29 17:24:07 +08:00
@runner.runner_class ( ' dify-service-api ' )
2024-12-14 17:51:11 +08:00
class DifyServiceAPIRunner ( runner . RequestRunner ) :
""" Dify Service API 对话请求器 """
dify_client : client . AsyncDifyServiceClient
2025-03-29 17:50:45 +08:00
def __init__ ( self , ap : app . Application , pipeline_config : dict ) :
self . ap = ap
self . pipeline_config = pipeline_config
2025-04-29 17:24:07 +08:00
valid_app_types = [ ' chat ' , ' agent ' , ' workflow ' ]
2025-05-10 18:04:58 +08:00
if self . pipeline_config [ ' ai ' ] [ ' dify-service-api ' ] [ ' app-type ' ] not in valid_app_types :
2024-12-16 23:54:56 +08:00
raise errors . DifyAPIError (
2025-04-29 17:24:07 +08:00
f ' 不支持的 Dify 应用类型: { self . pipeline_config [ " ai " ] [ " dify-service-api " ] [ " app-type " ] } '
2024-12-16 23:54:56 +08:00
)
2025-04-29 17:24:07 +08:00
api_key = self . pipeline_config [ ' ai ' ] [ ' dify-service-api ' ] [ ' api-key ' ]
2024-12-14 17:51:11 +08:00
self . dify_client = client . AsyncDifyServiceClient (
api_key = api_key ,
2025-04-29 17:24:07 +08:00
base_url = self . pipeline_config [ ' ai ' ] [ ' dify-service-api ' ] [ ' base-url ' ] ,
2024-12-14 17:51:11 +08:00
)
2025-08-14 22:32:22 +08:00
def _process_thinking_content (
2025-08-21 21:38:58 +08:00
self ,
content : str ,
2025-08-14 22:32:22 +08:00
) - > tuple [ str , str ] :
""" 处理思维链内容
2025-04-29 17:24:07 +08:00
2025-08-14 22:32:22 +08:00
Args :
content : 原始内容
Returns :
( 处理后的内容 , 提取的思维链内容 )
"""
remove_think = self . pipeline_config [ ' output ' ] . get ( ' misc ' , ' ' ) . get ( ' remove-think ' )
thinking_content = ' '
# 从 content 中提取 <think> 标签内容
if content and ' <think> ' in content and ' </think> ' in content :
import re
think_pattern = r ' <think>(.*?)</think> '
think_matches = re . findall ( think_pattern , content , re . DOTALL )
if think_matches :
thinking_content = ' \n ' . join ( think_matches )
# 移除 content 中的 <think> 标签
content = re . sub ( think_pattern , ' ' , content , flags = re . DOTALL ) . strip ( )
# 3. 根据 remove_think 参数决定是否保留思维链
if remove_think :
return content , ' '
else :
# 如果有思维链内容,将其以 <think> 格式添加到 content 开头
if thinking_content :
content = f ' <think> \n { thinking_content } \n </think> \n { content } ' . strip ( )
return content , thinking_content
2025-02-24 12:17:33 +08:00
2025-06-15 22:04:31 +08:00
async def _preprocess_user_message ( self , query : pipeline_query . Query ) - > tuple [ str , list [ str ] ] :
2024-12-14 17:51:11 +08:00
""" 预处理用户消息,提取纯文本,并将图片上传到 Dify 服务
2024-12-16 23:54:56 +08:00
2024-12-14 17:51:11 +08:00
Returns :
tuple [ str , list [ str ] ] : 纯文本和图片的 Dify 服务图片 ID
"""
2025-04-29 17:24:07 +08:00
plain_text = ' '
2025-11-10 21:42:09 +08:00
file_ids = [ ]
2025-03-02 18:49:32 +08:00
2024-12-14 17:51:11 +08:00
if isinstance ( query . user_message . content , list ) :
for ce in query . user_message . content :
2025-04-29 17:24:07 +08:00
if ce . type == ' text ' :
2024-12-14 17:51:11 +08:00
plain_text + = ce . text
2025-04-29 17:24:07 +08:00
elif ce . type == ' image_base64 ' :
2025-05-10 18:04:58 +08:00
image_b64 , image_format = await image . extract_b64_and_format ( ce . image_base64 )
2024-12-24 11:26:33 +08:00
file_bytes = base64 . b64decode ( image_b64 )
2025-04-29 17:24:07 +08:00
file = ( ' img.png ' , file_bytes , f ' image/ { image_format } ' )
2024-12-16 23:54:56 +08:00
file_upload_resp = await self . dify_client . upload_file (
file ,
2025-04-29 17:24:07 +08:00
f ' { query . session . launcher_type . value } _ { query . session . launcher_id } ' ,
2024-12-16 23:54:56 +08:00
)
2025-04-29 17:24:07 +08:00
image_id = file_upload_resp [ ' id ' ]
2025-11-10 21:42:09 +08:00
file_ids . append ( image_id )
# elif ce.type == "file_url":
# file_bytes = base64.b64decode(ce.file_url)
# file_upload_resp = await self.dify_client.upload_file(
# file_bytes,
# f'{query.session.launcher_type.value}_{query.session.launcher_id}',
# )
# file_id = file_upload_resp['id']
# file_ids.append(file_id)
2024-12-14 17:51:11 +08:00
elif isinstance ( query . user_message . content , str ) :
plain_text = query . user_message . content
2025-11-10 21:42:09 +08:00
# plain_text = "When the file content is readable, please read the content of this file. When the file is an image, describe the content of this image." if file_ids and not plain_text else plain_text
# plain_text = "The user message type cannot be parsed." if not file_ids and not plain_text else plain_text
# plain_text = plain_text if plain_text else "When the file content is readable, please read the content of this file. When the file is an image, describe the content of this image."
# print(self.pipeline_config['ai'])
plain_text = plain_text if plain_text else self . pipeline_config [ ' ai ' ] [ ' dify-service-api ' ] [ ' base-prompt ' ]
2024-12-14 17:51:11 +08:00
2025-11-10 21:42:09 +08:00
return plain_text , file_ids
2024-12-14 17:51:11 +08:00
2025-07-13 20:30:17 +08:00
async def _chat_messages (
self , query : pipeline_query . Query
) - > typing . AsyncGenerator [ provider_message . Message , None ] :
2024-12-14 17:51:11 +08:00
""" 调用聊天助手 """
2025-04-29 17:24:07 +08:00
cov_id = query . session . using_conversation . uuid or ' '
2025-05-20 15:32:04 +08:00
query . variables [ ' conversation_id ' ] = cov_id
2024-12-14 17:51:11 +08:00
2025-03-02 19:10:09 +08:00
plain_text , image_ids = await self . _preprocess_user_message ( query )
2024-12-14 17:51:11 +08:00
2024-12-16 23:54:56 +08:00
files = [
{
2025-04-29 17:24:07 +08:00
' type ' : ' image ' ,
' upload_file_id ' : image_id ,
2024-12-16 23:54:56 +08:00
}
for image_id in image_ids
]
2025-04-29 17:24:07 +08:00
mode = ' basic ' # 标记是基础编排还是工作流编排
2024-12-17 01:04:08 +08:00
basic_mode_pending_chunk = ' '
2025-03-12 19:13:04 +08:00
inputs = { }
2025-04-29 17:24:07 +08:00
2025-03-12 19:13:04 +08:00
inputs . update ( query . variables )
2025-05-09 01:28:43 +00:00
chunk = None # 初始化chunk变量, 防止在没有响应时引用错误
2025-03-12 19:13:04 +08:00
2024-12-17 00:41:28 +08:00
async for chunk in self . dify_client . chat_messages (
2025-03-12 19:13:04 +08:00
inputs = inputs ,
2024-12-16 23:54:56 +08:00
query = plain_text ,
2025-04-29 17:24:07 +08:00
user = f ' { query . session . launcher_type . value } _ { query . session . launcher_id } ' ,
2024-12-16 23:54:56 +08:00
conversation_id = cov_id ,
files = files ,
2025-05-12 18:21:08 +08:00
timeout = 120 ,
2024-12-17 00:41:28 +08:00
) :
2025-04-29 17:24:07 +08:00
self . ap . logger . debug ( ' dify-chat-chunk: ' + str ( chunk ) )
2024-12-17 01:04:08 +08:00
if chunk [ ' event ' ] == ' workflow_started ' :
2025-04-29 17:24:07 +08:00
mode = ' workflow '
2024-12-17 01:04:08 +08:00
2025-04-29 17:24:07 +08:00
if mode == ' workflow ' :
2024-12-17 01:04:08 +08:00
if chunk [ ' event ' ] == ' node_finished ' :
if chunk [ ' data ' ] [ ' node_type ' ] == ' answer ' :
2025-08-14 22:32:22 +08:00
content , _ = self . _process_thinking_content ( chunk [ ' data ' ] [ ' outputs ' ] [ ' answer ' ] )
2025-07-13 20:30:17 +08:00
yield provider_message . Message (
2025-04-29 17:24:07 +08:00
role = ' assistant ' ,
2025-08-14 22:32:22 +08:00
content = content ,
2024-12-17 01:04:08 +08:00
)
2025-04-29 17:24:07 +08:00
elif mode == ' basic ' :
2024-12-17 01:04:08 +08:00
if chunk [ ' event ' ] == ' message ' :
basic_mode_pending_chunk + = chunk [ ' answer ' ]
elif chunk [ ' event ' ] == ' message_end ' :
2025-08-14 22:32:22 +08:00
content , _ = self . _process_thinking_content ( basic_mode_pending_chunk )
2025-07-13 20:30:17 +08:00
yield provider_message . Message (
2025-04-29 17:24:07 +08:00
role = ' assistant ' ,
2025-08-14 22:32:22 +08:00
content = content ,
2024-12-17 00:41:28 +08:00
)
2024-12-17 01:04:08 +08:00
basic_mode_pending_chunk = ' '
2024-12-14 17:51:11 +08:00
2025-05-09 01:37:04 +00:00
if chunk is None :
2025-05-10 18:04:58 +08:00
raise errors . DifyAPIError ( ' Dify API 没有返回任何响应, 请检查网络连接和API配置 ' )
2024-12-14 17:51:11 +08:00
2025-04-29 17:24:07 +08:00
query . session . using_conversation . uuid = chunk [ ' conversation_id ' ]
2024-12-17 00:41:28 +08:00
async def _agent_chat_messages (
2025-06-15 22:04:31 +08:00
self , query : pipeline_query . Query
2025-07-13 20:30:17 +08:00
) - > typing . AsyncGenerator [ provider_message . Message , None ] :
2024-12-17 00:41:28 +08:00
""" 调用聊天助手 """
2025-04-29 17:24:07 +08:00
cov_id = query . session . using_conversation . uuid or ' '
2025-05-20 15:32:04 +08:00
query . variables [ ' conversation_id ' ] = cov_id
2024-12-14 17:51:11 +08:00
2025-03-02 19:10:09 +08:00
plain_text , image_ids = await self . _preprocess_user_message ( query )
2024-12-17 00:41:28 +08:00
files = [
{
2025-04-29 17:24:07 +08:00
' type ' : ' image ' ,
' transfer_method ' : ' local_file ' ,
' upload_file_id ' : image_id ,
2024-12-17 00:41:28 +08:00
}
for image_id in image_ids
]
2024-12-14 17:51:11 +08:00
2025-04-23 16:55:52 +08:00
ignored_events = [ ]
2024-12-17 00:41:28 +08:00
2025-03-12 19:13:04 +08:00
inputs = { }
2025-04-29 17:24:07 +08:00
2025-03-12 19:13:04 +08:00
inputs . update ( query . variables )
2025-04-23 16:55:52 +08:00
pending_agent_message = ' '
2025-05-10 17:47:14 +08:00
2025-05-09 01:28:43 +00:00
chunk = None # 初始化chunk变量, 防止在没有响应时引用错误
2025-04-23 16:55:52 +08:00
2024-12-17 00:41:28 +08:00
async for chunk in self . dify_client . chat_messages (
2025-03-12 19:13:04 +08:00
inputs = inputs ,
2024-12-17 00:41:28 +08:00
query = plain_text ,
2025-04-29 17:24:07 +08:00
user = f ' { query . session . launcher_type . value } _ { query . session . launcher_id } ' ,
response_mode = ' streaming ' ,
2024-12-17 00:41:28 +08:00
conversation_id = cov_id ,
files = files ,
2025-05-12 18:21:08 +08:00
timeout = 120 ,
2024-12-17 00:41:28 +08:00
) :
2025-04-29 17:24:07 +08:00
self . ap . logger . debug ( ' dify-agent-chunk: ' + str ( chunk ) )
2025-01-06 21:28:36 +08:00
2025-04-29 17:24:07 +08:00
if chunk [ ' event ' ] in ignored_events :
2024-12-17 00:41:28 +08:00
continue
2025-08-14 22:32:22 +08:00
if chunk [ ' event ' ] == ' agent_message ' or chunk [ ' event ' ] == ' message ' :
2025-04-23 16:55:52 +08:00
pending_agent_message + = chunk [ ' answer ' ]
else :
if pending_agent_message . strip ( ) != ' ' :
2025-05-10 18:04:58 +08:00
pending_agent_message = pending_agent_message . replace ( ' </details>Action: ' , ' </details> ' )
2025-08-14 22:32:22 +08:00
content , _ = self . _process_thinking_content ( pending_agent_message )
2025-07-13 20:30:17 +08:00
yield provider_message . Message (
2025-04-29 17:24:07 +08:00
role = ' assistant ' ,
2025-08-14 22:32:22 +08:00
content = content ,
2024-12-17 00:41:28 +08:00
)
2025-04-23 16:55:52 +08:00
pending_agent_message = ' '
2025-01-06 21:28:36 +08:00
2025-05-10 17:47:14 +08:00
if chunk [ ' event ' ] == ' agent_thought ' :
2025-05-10 18:04:58 +08:00
if chunk [ ' tool ' ] != ' ' and chunk [ ' observation ' ] != ' ' : # 工具调用结果,跳过
2025-04-23 16:55:52 +08:00
continue
2025-01-06 21:28:36 +08:00
2025-04-23 16:55:52 +08:00
if chunk [ ' tool ' ] :
2025-07-13 20:30:17 +08:00
msg = provider_message . Message (
2025-05-10 17:47:14 +08:00
role = ' assistant ' ,
2025-04-23 16:55:52 +08:00
tool_calls = [
2025-07-13 20:30:17 +08:00
provider_message . ToolCall (
2025-04-23 16:55:52 +08:00
id = chunk [ ' id ' ] ,
2025-05-10 17:47:14 +08:00
type = ' function ' ,
2025-07-13 20:30:17 +08:00
function = provider_message . FunctionCall (
2025-05-10 17:47:14 +08:00
name = chunk [ ' tool ' ] ,
2025-04-23 16:55:52 +08:00
arguments = json . dumps ( { } ) ,
) ,
)
] ,
)
yield msg
if chunk [ ' event ' ] == ' message_file ' :
if chunk [ ' type ' ] == ' image ' and chunk [ ' belongs_to ' ] == ' assistant ' :
base_url = self . dify_client . base_url
2025-01-06 21:28:36 +08:00
2025-04-23 16:55:52 +08:00
if base_url . endswith ( ' /v1 ' ) :
base_url = base_url [ : - 3 ]
image_url = base_url + chunk [ ' url ' ]
2025-07-13 20:30:17 +08:00
yield provider_message . Message (
2025-05-10 17:47:14 +08:00
role = ' assistant ' ,
2025-07-13 20:30:17 +08:00
content = [ provider_message . ContentElement . from_image_url ( image_url ) ] ,
2025-04-23 16:55:52 +08:00
)
if chunk [ ' event ' ] == ' error ' :
2025-05-10 17:47:14 +08:00
raise errors . DifyAPIError ( ' dify 服务错误: ' + chunk [ ' message ' ] )
2025-05-09 01:37:04 +00:00
if chunk is None :
2025-05-10 18:04:58 +08:00
raise errors . DifyAPIError ( ' Dify API 没有返回任何响应, 请检查网络连接和API配置 ' )
2024-12-17 00:41:28 +08:00
2025-04-29 17:24:07 +08:00
query . session . using_conversation . uuid = chunk [ ' conversation_id ' ]
2024-12-14 17:51:11 +08:00
2025-06-15 22:04:31 +08:00
async def _workflow_messages (
self , query : pipeline_query . Query
2025-07-13 20:30:17 +08:00
) - > typing . AsyncGenerator [ provider_message . Message , None ] :
2024-12-14 17:51:11 +08:00
""" 调用工作流 """
if not query . session . using_conversation . uuid :
query . session . using_conversation . uuid = str ( uuid . uuid4 ( ) )
2025-04-29 17:24:07 +08:00
query . variables [ ' conversation_id ' ] = query . session . using_conversation . uuid
2024-12-14 17:51:11 +08:00
2025-03-02 19:10:53 +08:00
plain_text , image_ids = await self . _preprocess_user_message ( query )
2024-12-14 17:51:11 +08:00
2024-12-16 23:54:56 +08:00
files = [
{
2025-04-29 17:24:07 +08:00
' type ' : ' image ' ,
' transfer_method ' : ' local_file ' ,
' upload_file_id ' : image_id ,
2024-12-16 23:54:56 +08:00
}
for image_id in image_ids
]
2025-04-29 17:24:07 +08:00
ignored_events = [ ' text_chunk ' , ' workflow_started ' ]
2024-12-16 23:54:56 +08:00
2025-03-12 19:13:04 +08:00
inputs = { # these variables are legacy variables, we need to keep them for compatibility
2025-04-29 17:24:07 +08:00
' langbot_user_message_text ' : plain_text ,
' langbot_session_id ' : query . variables [ ' session_id ' ] ,
' langbot_conversation_id ' : query . variables [ ' conversation_id ' ] ,
' langbot_msg_create_time ' : query . variables [ ' msg_create_time ' ] ,
2025-03-12 19:13:04 +08:00
}
2025-04-29 17:24:07 +08:00
2025-03-12 19:13:04 +08:00
inputs . update ( query . variables )
2024-12-16 23:54:56 +08:00
async for chunk in self . dify_client . workflow_run (
2025-03-12 19:13:04 +08:00
inputs = inputs ,
2025-04-29 17:24:07 +08:00
user = f ' { query . session . launcher_type . value } _ { query . session . launcher_id } ' ,
2024-12-16 23:54:56 +08:00
files = files ,
2025-05-12 18:21:08 +08:00
timeout = 120 ,
2024-12-16 23:54:56 +08:00
) :
2025-04-29 17:24:07 +08:00
self . ap . logger . debug ( ' dify-workflow-chunk: ' + str ( chunk ) )
if chunk [ ' event ' ] in ignored_events :
2024-12-14 17:51:11 +08:00
continue
2025-04-29 17:24:07 +08:00
if chunk [ ' event ' ] == ' node_started ' :
2025-05-10 18:04:58 +08:00
if chunk [ ' data ' ] [ ' node_type ' ] == ' start ' or chunk [ ' data ' ] [ ' node_type ' ] == ' end ' :
2024-12-14 17:51:11 +08:00
continue
2025-07-13 20:30:17 +08:00
msg = provider_message . Message (
2025-04-29 17:24:07 +08:00
role = ' assistant ' ,
2024-12-14 17:51:11 +08:00
content = None ,
2024-12-16 23:54:56 +08:00
tool_calls = [
2025-07-13 20:30:17 +08:00
provider_message . ToolCall (
2025-04-29 17:24:07 +08:00
id = chunk [ ' data ' ] [ ' node_id ' ] ,
type = ' function ' ,
2025-07-13 20:30:17 +08:00
function = provider_message . FunctionCall (
2025-04-29 17:24:07 +08:00
name = chunk [ ' data ' ] [ ' title ' ] ,
2024-12-16 23:54:56 +08:00
arguments = json . dumps ( { } ) ,
) ,
)
] ,
2024-12-14 17:51:11 +08:00
)
yield msg
2025-04-29 17:24:07 +08:00
elif chunk [ ' event ' ] == ' workflow_finished ' :
2024-12-17 01:04:08 +08:00
if chunk [ ' data ' ] [ ' error ' ] :
raise errors . DifyAPIError ( chunk [ ' data ' ] [ ' error ' ] )
2025-08-14 22:32:22 +08:00
content , _ = self . _process_thinking_content ( chunk [ ' data ' ] [ ' outputs ' ] [ ' summary ' ] )
2024-12-14 17:51:11 +08:00
2025-07-13 20:30:17 +08:00
msg = provider_message . Message (
2025-04-29 17:24:07 +08:00
role = ' assistant ' ,
2025-08-14 22:32:22 +08:00
content = content ,
2024-12-14 17:51:11 +08:00
)
yield msg
2025-08-21 21:38:58 +08:00
async def _chat_messages_chunk (
2025-08-24 21:40:02 +08:00
self , query : pipeline_query . Query
) - > typing . AsyncGenerator [ provider_message . MessageChunk , None ] :
2025-08-15 00:50:32 +08:00
""" 调用聊天助手 """
cov_id = query . session . using_conversation . uuid or ' '
query . variables [ ' conversation_id ' ] = cov_id
plain_text , image_ids = await self . _preprocess_user_message ( query )
files = [
{
' type ' : ' image ' ,
' transfer_method ' : ' local_file ' ,
' upload_file_id ' : image_id ,
}
for image_id in image_ids
]
basic_mode_pending_chunk = ' '
inputs = { }
inputs . update ( query . variables )
message_idx = 0
chunk = None # 初始化chunk变量, 防止在没有响应时引用错误
is_final = False
think_start = False
think_end = False
remove_think = self . pipeline_config [ ' output ' ] . get ( ' misc ' , ' ' ) . get ( ' remove-think ' )
async for chunk in self . dify_client . chat_messages (
inputs = inputs ,
query = plain_text ,
user = f ' { query . session . launcher_type . value } _ { query . session . launcher_id } ' ,
conversation_id = cov_id ,
files = files ,
timeout = 120 ,
) :
self . ap . logger . debug ( ' dify-chat-chunk: ' + str ( chunk ) )
# if chunk['event'] == 'workflow_started':
# mode = 'workflow'
# if mode == 'workflow':
# elif mode == 'basic':
# 因为都只是返回的 message也没有工具调用什么的, 暂时不分类
if chunk [ ' event ' ] == ' message ' :
message_idx + = 1
if remove_think :
2025-08-15 00:55:39 +08:00
if ' <think> ' in chunk [ ' answer ' ] and not think_start :
2025-08-15 00:50:32 +08:00
think_start = True
continue
if ' </think> ' in chunk [ ' answer ' ] and not think_end :
import re
2025-08-21 21:38:58 +08:00
2025-08-15 00:50:32 +08:00
content = re . sub ( r ' ^ \ n</think> ' , ' ' , chunk [ ' answer ' ] )
basic_mode_pending_chunk + = content
think_end = True
elif think_end :
basic_mode_pending_chunk + = chunk [ ' answer ' ]
if think_start :
continue
else :
basic_mode_pending_chunk + = chunk [ ' answer ' ]
if chunk [ ' event ' ] == ' message_end ' :
is_final = True
if is_final or message_idx % 8 == 0 :
# content, _ = self._process_thinking_content(basic_mode_pending_chunk)
2025-08-24 21:40:02 +08:00
yield provider_message . MessageChunk (
2025-08-15 00:50:32 +08:00
role = ' assistant ' ,
content = basic_mode_pending_chunk ,
is_final = is_final ,
)
if chunk is None :
raise errors . DifyAPIError ( ' Dify API 没有返回任何响应, 请检查网络连接和API配置 ' )
query . session . using_conversation . uuid = chunk [ ' conversation_id ' ]
async def _agent_chat_messages_chunk (
2025-08-24 21:40:02 +08:00
self , query : pipeline_query . Query
) - > typing . AsyncGenerator [ provider_message . MessageChunk , None ] :
2025-08-15 00:50:32 +08:00
""" 调用聊天助手 """
cov_id = query . session . using_conversation . uuid or ' '
query . variables [ ' conversation_id ' ] = cov_id
plain_text , image_ids = await self . _preprocess_user_message ( query )
files = [
{
' type ' : ' image ' ,
' transfer_method ' : ' local_file ' ,
' upload_file_id ' : image_id ,
}
for image_id in image_ids
]
ignored_events = [ ]
inputs = { }
inputs . update ( query . variables )
pending_agent_message = ' '
chunk = None # 初始化chunk变量, 防止在没有响应时引用错误
message_idx = 0
is_final = False
think_start = False
think_end = False
remove_think = self . pipeline_config [ ' output ' ] . get ( ' misc ' , ' ' ) . get ( ' remove-think ' )
async for chunk in self . dify_client . chat_messages (
inputs = inputs ,
query = plain_text ,
user = f ' { query . session . launcher_type . value } _ { query . session . launcher_id } ' ,
response_mode = ' streaming ' ,
conversation_id = cov_id ,
files = files ,
timeout = 120 ,
) :
self . ap . logger . debug ( ' dify-agent-chunk: ' + str ( chunk ) )
if chunk [ ' event ' ] in ignored_events :
continue
if chunk [ ' event ' ] == ' agent_message ' :
message_idx + = 1
if remove_think :
if ' <think> ' in chunk [ ' answer ' ] and not think_start :
think_start = True
continue
if ' </think> ' in chunk [ ' answer ' ] and not think_end :
import re
2025-08-21 21:38:58 +08:00
2025-08-15 00:50:32 +08:00
content = re . sub ( r ' ^ \ n</think> ' , ' ' , chunk [ ' answer ' ] )
pending_agent_message + = content
think_end = True
2025-08-19 23:23:00 +08:00
elif think_end or not think_start :
2025-08-15 00:50:32 +08:00
pending_agent_message + = chunk [ ' answer ' ]
if think_start :
continue
else :
pending_agent_message + = chunk [ ' answer ' ]
elif chunk [ ' event ' ] == ' message_end ' :
is_final = True
else :
if chunk [ ' event ' ] == ' agent_thought ' :
if chunk [ ' tool ' ] != ' ' and chunk [ ' observation ' ] != ' ' : # 工具调用结果,跳过
continue
message_idx + = 1
if chunk [ ' tool ' ] :
2025-08-24 21:40:02 +08:00
msg = provider_message . MessageChunk (
2025-08-15 00:50:32 +08:00
role = ' assistant ' ,
tool_calls = [
2025-08-24 21:40:02 +08:00
provider_message . ToolCall (
2025-08-15 00:50:32 +08:00
id = chunk [ ' id ' ] ,
type = ' function ' ,
2025-08-24 21:40:02 +08:00
function = provider_message . FunctionCall (
2025-08-15 00:50:32 +08:00
name = chunk [ ' tool ' ] ,
arguments = json . dumps ( { } ) ,
) ,
)
] ,
)
yield msg
if chunk [ ' event ' ] == ' message_file ' :
message_idx + = 1
if chunk [ ' type ' ] == ' image ' and chunk [ ' belongs_to ' ] == ' assistant ' :
base_url = self . dify_client . base_url
if base_url . endswith ( ' /v1 ' ) :
base_url = base_url [ : - 3 ]
image_url = base_url + chunk [ ' url ' ]
2025-08-24 21:40:02 +08:00
yield provider_message . MessageChunk (
2025-08-15 00:50:32 +08:00
role = ' assistant ' ,
2025-08-24 21:40:02 +08:00
content = [ provider_message . ContentElement . from_image_url ( image_url ) ] ,
2025-08-15 00:50:32 +08:00
is_final = is_final ,
)
if chunk [ ' event ' ] == ' error ' :
raise errors . DifyAPIError ( ' dify 服务错误: ' + chunk [ ' message ' ] )
if message_idx % 8 == 0 or is_final :
2025-08-24 21:40:02 +08:00
yield provider_message . MessageChunk (
2025-08-15 00:50:32 +08:00
role = ' assistant ' ,
content = pending_agent_message ,
is_final = is_final ,
)
if chunk is None :
raise errors . DifyAPIError ( ' Dify API 没有返回任何响应, 请检查网络连接和API配置 ' )
query . session . using_conversation . uuid = chunk [ ' conversation_id ' ]
2025-08-21 21:38:58 +08:00
async def _workflow_messages_chunk (
2025-08-24 21:40:02 +08:00
self , query : pipeline_query . Query
) - > typing . AsyncGenerator [ provider_message . MessageChunk , None ] :
2025-08-15 00:50:32 +08:00
""" 调用工作流 """
if not query . session . using_conversation . uuid :
query . session . using_conversation . uuid = str ( uuid . uuid4 ( ) )
query . variables [ ' conversation_id ' ] = query . session . using_conversation . uuid
plain_text , image_ids = await self . _preprocess_user_message ( query )
files = [
{
' type ' : ' image ' ,
' transfer_method ' : ' local_file ' ,
' upload_file_id ' : image_id ,
}
for image_id in image_ids
]
ignored_events = [ ' workflow_started ' ]
inputs = { # these variables are legacy variables, we need to keep them for compatibility
' langbot_user_message_text ' : plain_text ,
' langbot_session_id ' : query . variables [ ' session_id ' ] ,
' langbot_conversation_id ' : query . variables [ ' conversation_id ' ] ,
' langbot_msg_create_time ' : query . variables [ ' msg_create_time ' ] ,
}
inputs . update ( query . variables )
messsage_idx = 0
is_final = False
think_start = False
think_end = False
workflow_contents = ' '
remove_think = self . pipeline_config [ ' output ' ] . get ( ' misc ' , ' ' ) . get ( ' remove-think ' )
async for chunk in self . dify_client . workflow_run (
inputs = inputs ,
user = f ' { query . session . launcher_type . value } _ { query . session . launcher_id } ' ,
files = files ,
timeout = 120 ,
) :
self . ap . logger . debug ( ' dify-workflow-chunk: ' + str ( chunk ) )
if chunk [ ' event ' ] in ignored_events :
continue
if chunk [ ' event ' ] == ' workflow_finished ' :
is_final = True
if chunk [ ' data ' ] [ ' error ' ] :
raise errors . DifyAPIError ( chunk [ ' data ' ] [ ' error ' ] )
if chunk [ ' event ' ] == ' text_chunk ' :
messsage_idx + = 1
if remove_think :
if ' <think> ' in chunk [ ' data ' ] [ ' text ' ] and not think_start :
think_start = True
continue
if ' </think> ' in chunk [ ' data ' ] [ ' text ' ] and not think_end :
import re
2025-08-21 21:38:58 +08:00
2025-08-15 00:50:32 +08:00
content = re . sub ( r ' ^ \ n</think> ' , ' ' , chunk [ ' data ' ] [ ' text ' ] )
workflow_contents + = content
think_end = True
elif think_end :
workflow_contents + = chunk [ ' data ' ] [ ' text ' ]
if think_start :
continue
else :
workflow_contents + = chunk [ ' data ' ] [ ' text ' ]
if chunk [ ' event ' ] == ' node_started ' :
if chunk [ ' data ' ] [ ' node_type ' ] == ' start ' or chunk [ ' data ' ] [ ' node_type ' ] == ' end ' :
continue
messsage_idx + = 1
2025-08-24 21:40:02 +08:00
msg = provider_message . MessageChunk (
2025-08-15 00:50:32 +08:00
role = ' assistant ' ,
content = None ,
tool_calls = [
2025-08-24 21:40:02 +08:00
provider_message . ToolCall (
2025-08-15 00:50:32 +08:00
id = chunk [ ' data ' ] [ ' node_id ' ] ,
type = ' function ' ,
2025-08-24 21:40:02 +08:00
function = provider_message . FunctionCall (
2025-08-15 00:50:32 +08:00
name = chunk [ ' data ' ] [ ' title ' ] ,
arguments = json . dumps ( { } ) ,
) ,
)
] ,
2024-12-14 17:51:11 +08:00
)
yield msg
2025-08-15 00:50:32 +08:00
if messsage_idx % 8 == 0 or is_final :
2025-08-24 21:40:02 +08:00
yield provider_message . MessageChunk (
2025-08-15 00:50:32 +08:00
role = ' assistant ' ,
content = workflow_contents ,
is_final = is_final ,
)
2025-07-13 20:30:17 +08:00
async def run ( self , query : pipeline_query . Query ) - > typing . AsyncGenerator [ provider_message . Message , None ] :
2024-12-14 17:51:11 +08:00
""" 运行请求 """
2025-08-15 00:50:32 +08:00
if await query . adapter . is_stream_output_supported ( ) :
msg_idx = 0
if self . pipeline_config [ ' ai ' ] [ ' dify-service-api ' ] [ ' app-type ' ] == ' chat ' :
async for msg in self . _chat_messages_chunk ( query ) :
msg_idx + = 1
msg . msg_sequence = msg_idx
yield msg
elif self . pipeline_config [ ' ai ' ] [ ' dify-service-api ' ] [ ' app-type ' ] == ' agent ' :
async for msg in self . _agent_chat_messages_chunk ( query ) :
msg_idx + = 1
msg . msg_sequence = msg_idx
yield msg
elif self . pipeline_config [ ' ai ' ] [ ' dify-service-api ' ] [ ' app-type ' ] == ' workflow ' :
async for msg in self . _workflow_messages_chunk ( query ) :
msg_idx + = 1
msg . msg_sequence = msg_idx
yield msg
else :
raise errors . DifyAPIError (
f ' 不支持的 Dify 应用类型: { self . pipeline_config [ " ai " ] [ " dify-service-api " ] [ " app-type " ] } '
)
2024-12-14 17:51:11 +08:00
else :
2025-08-15 00:50:32 +08:00
if self . pipeline_config [ ' ai ' ] [ ' dify-service-api ' ] [ ' app-type ' ] == ' chat ' :
async for msg in self . _chat_messages ( query ) :
yield msg
elif self . pipeline_config [ ' ai ' ] [ ' dify-service-api ' ] [ ' app-type ' ] == ' agent ' :
async for msg in self . _agent_chat_messages ( query ) :
yield msg
elif self . pipeline_config [ ' ai ' ] [ ' dify-service-api ' ] [ ' app-type ' ] == ' workflow ' :
async for msg in self . _workflow_messages ( query ) :
yield msg
else :
raise errors . DifyAPIError (
f ' 不支持的 Dify 应用类型: { self . pipeline_config [ " ai " ] [ " dify-service-api " ] [ " app-type " ] } '
2025-08-21 21:38:58 +08:00
)