mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-08-13 22:20:58 +00:00
ruff3
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
参考 astrbot 的 deerflow_api_client 实现,使用 httpx 适配 LangBot 风格。
|
参考 astrbot 的 deerflow_api_client 实现,使用 httpx 适配 LangBot 风格。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import codecs
|
import codecs
|
||||||
|
|||||||
@@ -26,8 +26,5 @@ class DeerFlowAPIError(Exception):
|
|||||||
|
|
||||||
msg = f'DeerFlow {operation} failed: status={status}, url={url}, body={body}'
|
msg = f'DeerFlow {operation} failed: status={status}, url={url}, body={body}'
|
||||||
if thread_id is not None:
|
if thread_id is not None:
|
||||||
msg = (
|
msg = f'DeerFlow {operation} failed: thread_id={thread_id}, status={status}, url={url}, body={body}'
|
||||||
f'DeerFlow {operation} failed: thread_id={thread_id}, '
|
|
||||||
f'status={status}, url={url}, body={body}'
|
|
||||||
)
|
|
||||||
super().__init__(msg)
|
super().__init__(msg)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
参考 astrbot 实现的 deerflow_stream_utils。
|
参考 astrbot 实现的 deerflow_stream_utils。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import typing
|
import typing
|
||||||
@@ -60,9 +61,7 @@ def is_ai_message(message: dict[str, typing.Any]) -> bool:
|
|||||||
msg_type = str(message.get('type', '')).lower()
|
msg_type = str(message.get('type', '')).lower()
|
||||||
if msg_type in {'ai', 'assistant', 'aimessage', 'aimessagechunk'}:
|
if msg_type in {'ai', 'assistant', 'aimessage', 'aimessagechunk'}:
|
||||||
return True
|
return True
|
||||||
if 'ai' in msg_type and all(
|
if 'ai' in msg_type and all(token not in msg_type for token in ('human', 'tool', 'system')):
|
||||||
token not in msg_type for token in ('human', 'tool', 'system')
|
|
||||||
):
|
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
- 支持 streaming/非流式两种输出
|
- 支持 streaming/非流式两种输出
|
||||||
- 处理 values / messages-tuple / custom 三种事件
|
- 处理 values / messages-tuple / custom 三种事件
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
@@ -32,6 +33,7 @@ _MAX_VALUES_HISTORY = 200
|
|||||||
@dataclass
|
@dataclass
|
||||||
class _StreamState:
|
class _StreamState:
|
||||||
"""流式状态跟踪"""
|
"""流式状态跟踪"""
|
||||||
|
|
||||||
latest_text: str = ''
|
latest_text: str = ''
|
||||||
prev_text_for_streaming: str = ''
|
prev_text_for_streaming: str = ''
|
||||||
clarification_text: str = ''
|
clarification_text: str = ''
|
||||||
@@ -258,9 +260,7 @@ class DeerFlowAPIRunner(runner.RequestRunner):
|
|||||||
thread = await self.deerflow_client.create_thread(timeout=min(30, self.timeout))
|
thread = await self.deerflow_client.create_thread(timeout=min(30, self.timeout))
|
||||||
thread_id = thread.get('thread_id', '')
|
thread_id = thread.get('thread_id', '')
|
||||||
if not thread_id:
|
if not thread_id:
|
||||||
raise errors.DeerFlowAPIError(
|
raise errors.DeerFlowAPIError(message=f'DeerFlow create thread 返回数据缺少 thread_id: {thread}')
|
||||||
message=f'DeerFlow create thread 返回数据缺少 thread_id: {thread}'
|
|
||||||
)
|
|
||||||
|
|
||||||
query.session.using_conversation.uuid = thread_id
|
query.session.using_conversation.uuid = thread_id
|
||||||
return thread_id
|
return thread_id
|
||||||
@@ -298,9 +298,7 @@ class DeerFlowAPIRunner(runner.RequestRunner):
|
|||||||
if new_messages:
|
if new_messages:
|
||||||
state.run_values_messages.extend(new_messages)
|
state.run_values_messages.extend(new_messages)
|
||||||
if len(state.run_values_messages) > _MAX_VALUES_HISTORY:
|
if len(state.run_values_messages) > _MAX_VALUES_HISTORY:
|
||||||
state.run_values_messages = state.run_values_messages[
|
state.run_values_messages = state.run_values_messages[-_MAX_VALUES_HISTORY:]
|
||||||
-_MAX_VALUES_HISTORY:
|
|
||||||
]
|
|
||||||
latest_text = stream_utils.extract_latest_ai_text(state.run_values_messages)
|
latest_text = stream_utils.extract_latest_ai_text(state.run_values_messages)
|
||||||
if latest_text:
|
if latest_text:
|
||||||
state.has_values_text = True
|
state.has_values_text = True
|
||||||
@@ -342,17 +340,13 @@ class DeerFlowAPIRunner(runner.RequestRunner):
|
|||||||
text = stream_utils.extract_text(latest_ai.get('content'))
|
text = stream_utils.extract_text(latest_ai.get('content'))
|
||||||
if text:
|
if text:
|
||||||
if state.timed_out:
|
if state.timed_out:
|
||||||
text += (
|
text += f'\n\nDeerFlow stream 在 {self.timeout}s 后超时,返回部分结果。'
|
||||||
f'\n\nDeerFlow stream 在 {self.timeout}s 后超时,返回部分结果。'
|
|
||||||
)
|
|
||||||
return text
|
return text
|
||||||
|
|
||||||
if state.latest_text:
|
if state.latest_text:
|
||||||
text = state.latest_text
|
text = state.latest_text
|
||||||
if state.timed_out:
|
if state.timed_out:
|
||||||
text += (
|
text += f'\n\nDeerFlow stream 在 {self.timeout}s 后超时,返回部分结果。'
|
||||||
f'\n\nDeerFlow stream 在 {self.timeout}s 后超时,返回部分结果。'
|
|
||||||
)
|
|
||||||
return text
|
return text
|
||||||
|
|
||||||
# 提取任务失败信息作兜底
|
# 提取任务失败信息作兜底
|
||||||
@@ -401,11 +395,7 @@ class DeerFlowAPIRunner(runner.RequestRunner):
|
|||||||
if event_type == 'values':
|
if event_type == 'values':
|
||||||
new_full = self._handle_values_event(data, state)
|
new_full = self._handle_values_event(data, state)
|
||||||
if new_full and new_full != prev_text:
|
if new_full and new_full != prev_text:
|
||||||
delta = (
|
delta = new_full[len(prev_text) :] if new_full.startswith(prev_text) else new_full
|
||||||
new_full[len(prev_text):]
|
|
||||||
if new_full.startswith(prev_text)
|
|
||||||
else new_full
|
|
||||||
)
|
|
||||||
prev_text = new_full
|
prev_text = new_full
|
||||||
if delta:
|
if delta:
|
||||||
message_idx += 1
|
message_idx += 1
|
||||||
@@ -435,16 +425,12 @@ class DeerFlowAPIRunner(runner.RequestRunner):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
if event_type == 'error':
|
if event_type == 'error':
|
||||||
raise errors.DeerFlowAPIError(
|
raise errors.DeerFlowAPIError(message=f'DeerFlow stream error event: {data}')
|
||||||
message=f'DeerFlow stream error event: {data}'
|
|
||||||
)
|
|
||||||
|
|
||||||
if event_type == 'end':
|
if event_type == 'end':
|
||||||
break
|
break
|
||||||
except (asyncio.TimeoutError, TimeoutError):
|
except (asyncio.TimeoutError, TimeoutError):
|
||||||
self.ap.logger.warning(
|
self.ap.logger.warning(f'DeerFlow stream timed out after {self.timeout}s for thread_id={thread_id}')
|
||||||
f'DeerFlow stream timed out after {self.timeout}s for thread_id={thread_id}'
|
|
||||||
)
|
|
||||||
state.timed_out = True
|
state.timed_out = True
|
||||||
|
|
||||||
# 最终消息
|
# 最终消息
|
||||||
@@ -495,16 +481,12 @@ class DeerFlowAPIRunner(runner.RequestRunner):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
if event_type == 'error':
|
if event_type == 'error':
|
||||||
raise errors.DeerFlowAPIError(
|
raise errors.DeerFlowAPIError(message=f'DeerFlow stream error event: {data}')
|
||||||
message=f'DeerFlow stream error event: {data}'
|
|
||||||
)
|
|
||||||
|
|
||||||
if event_type == 'end':
|
if event_type == 'end':
|
||||||
break
|
break
|
||||||
except (asyncio.TimeoutError, TimeoutError):
|
except (asyncio.TimeoutError, TimeoutError):
|
||||||
self.ap.logger.warning(
|
self.ap.logger.warning(f'DeerFlow stream timed out after {self.timeout}s for thread_id={thread_id}')
|
||||||
f'DeerFlow stream timed out after {self.timeout}s for thread_id={thread_id}'
|
|
||||||
)
|
|
||||||
state.timed_out = True
|
state.timed_out = True
|
||||||
|
|
||||||
final_text = self._build_final_text(state)
|
final_text = self._build_final_text(state)
|
||||||
|
|||||||
@@ -35,9 +35,7 @@ class WeKnoraAPIRunner(runner.RequestRunner):
|
|||||||
|
|
||||||
base_url = self.pipeline_config['ai']['weknora-api'].get('base-url', '').strip()
|
base_url = self.pipeline_config['ai']['weknora-api'].get('base-url', '').strip()
|
||||||
if not base_url:
|
if not base_url:
|
||||||
raise errors.WeKnoraAPIError(
|
raise errors.WeKnoraAPIError('WeKnora Base URL 未配置,请填入服务器地址,例如 http://localhost:8080/api/v1')
|
||||||
'WeKnora Base URL 未配置,请填入服务器地址,例如 http://localhost:8080/api/v1'
|
|
||||||
)
|
|
||||||
|
|
||||||
self.weknora_client = client.AsyncWeKnoraClient(
|
self.weknora_client = client.AsyncWeKnoraClient(
|
||||||
api_key=api_key,
|
api_key=api_key,
|
||||||
@@ -65,9 +63,7 @@ class WeKnoraAPIRunner(runner.RequestRunner):
|
|||||||
|
|
||||||
if not session_id:
|
if not session_id:
|
||||||
user_tag = f'{query.session.launcher_type.value}_{query.session.launcher_id}'
|
user_tag = f'{query.session.launcher_type.value}_{query.session.launcher_id}'
|
||||||
session_id = await self.weknora_client.create_session(
|
session_id = await self.weknora_client.create_session(title=f'IM Chat - {user_tag}')
|
||||||
title=f'IM Chat - {user_tag}'
|
|
||||||
)
|
|
||||||
query.session.using_conversation.uuid = session_id
|
query.session.using_conversation.uuid = session_id
|
||||||
|
|
||||||
return session_id
|
return session_id
|
||||||
@@ -343,9 +339,7 @@ class WeKnoraAPIRunner(runner.RequestRunner):
|
|||||||
msg.msg_sequence = msg_idx
|
msg.msg_sequence = msg_idx
|
||||||
yield msg
|
yield msg
|
||||||
else:
|
else:
|
||||||
raise errors.WeKnoraAPIError(
|
raise errors.WeKnoraAPIError(f'不支持的 WeKnora 应用类型: {app_type}')
|
||||||
f'不支持的 WeKnora 应用类型: {app_type}'
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
if app_type == 'agent':
|
if app_type == 'agent':
|
||||||
async for msg in self._agent_chat_messages(query):
|
async for msg in self._agent_chat_messages(query):
|
||||||
@@ -354,6 +348,4 @@ class WeKnoraAPIRunner(runner.RequestRunner):
|
|||||||
async for msg in self._chat_messages(query):
|
async for msg in self._chat_messages(query):
|
||||||
yield msg
|
yield msg
|
||||||
else:
|
else:
|
||||||
raise errors.WeKnoraAPIError(
|
raise errors.WeKnoraAPIError(f'不支持的 WeKnora 应用类型: {app_type}')
|
||||||
f'不支持的 WeKnora 应用类型: {app_type}'
|
|
||||||
)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user