Compare commits

...

5 Commits

Author SHA1 Message Date
Junyan Qin
f1ddddfe00 chore: bump version 4.3.8 2025-10-10 22:50:57 +08:00
Junyan Qin
4e61302156 fix: datetime serialization error in emit_event (#1713) 2025-10-10 22:37:39 +08:00
Junyan Qin
9e3cf418ba perf: output pipeline error in en 2025-10-10 17:55:49 +08:00
Junyan Qin
3e29ec7892 perf: allow not set llm model (#1703) 2025-10-10 16:34:01 +08:00
Junyan Qin
f452742cd2 fix: bad Plain component init in wechatpad (#1712) 2025-10-10 14:48:21 +08:00
6 changed files with 37 additions and 25 deletions

View File

@@ -213,7 +213,7 @@ class RuntimePipeline:
await self._execute_from_stage(0, query) await self._execute_from_stage(0, query)
except Exception as e: except Exception as e:
inst_name = query.current_stage_name if query.current_stage_name else 'unknown' inst_name = query.current_stage_name if query.current_stage_name else 'unknown'
self.ap.logger.error(f'处理请求时出错 query_id={query.query_id} stage={inst_name} : {e}') self.ap.logger.error(f'Error processing query {query.query_id} stage={inst_name} : {e}')
self.ap.logger.error(f'Traceback: {traceback.format_exc()}') self.ap.logger.error(f'Traceback: {traceback.format_exc()}')
finally: finally:
self.ap.logger.debug(f'Query {query.query_id} processed') self.ap.logger.debug(f'Query {query.query_id} processed')

View File

@@ -35,11 +35,17 @@ class PreProcessor(stage.PipelineStage):
session = await self.ap.sess_mgr.get_session(query) session = await self.ap.sess_mgr.get_session(query)
# When not local-agent, llm_model is None # When not local-agent, llm_model is None
llm_model = ( try:
await self.ap.model_mgr.get_model_by_uuid(query.pipeline_config['ai']['local-agent']['model']) llm_model = (
if selected_runner == 'local-agent' await self.ap.model_mgr.get_model_by_uuid(query.pipeline_config['ai']['local-agent']['model'])
else None if selected_runner == 'local-agent'
) else None
)
except ValueError:
self.ap.logger.warning(
f'LLM model {query.pipeline_config["ai"]["local-agent"]["model"] + " "}not found or not configured'
)
llm_model = None
conversation = await self.ap.sess_mgr.get_conversation( conversation = await self.ap.sess_mgr.get_conversation(
query, query,
@@ -54,7 +60,7 @@ class PreProcessor(stage.PipelineStage):
query.prompt = conversation.prompt.copy() query.prompt = conversation.prompt.copy()
query.messages = conversation.messages.copy() query.messages = conversation.messages.copy()
if selected_runner == 'local-agent': if selected_runner == 'local-agent' and llm_model:
query.use_funcs = [] query.use_funcs = []
query.use_llm_model_uuid = llm_model.model_entity.uuid query.use_llm_model_uuid = llm_model.model_entity.uuid
@@ -72,7 +78,11 @@ class PreProcessor(stage.PipelineStage):
# Check if this model supports vision, if not, remove all images # Check if this model supports vision, if not, remove all images
# TODO this checking should be performed in runner, and in this stage, the image should be reserved # TODO this checking should be performed in runner, and in this stage, the image should be reserved
if selected_runner == 'local-agent' and not llm_model.model_entity.abilities.__contains__('vision'): if (
selected_runner == 'local-agent'
and llm_model
and not llm_model.model_entity.abilities.__contains__('vision')
):
for msg in query.messages: for msg in query.messages:
if isinstance(msg.content, list): if isinstance(msg.content, list):
for me in msg.content: for me in msg.content:
@@ -89,7 +99,9 @@ class PreProcessor(stage.PipelineStage):
content_list.append(provider_message.ContentElement.from_text(me.text)) content_list.append(provider_message.ContentElement.from_text(me.text))
plain_text += me.text plain_text += me.text
elif isinstance(me, platform_message.Image): elif isinstance(me, platform_message.Image):
if selected_runner != 'local-agent' or llm_model.model_entity.abilities.__contains__('vision'): if selected_runner != 'local-agent' or (
llm_model and llm_model.model_entity.abilities.__contains__('vision')
):
if me.base64 is not None: if me.base64 is not None:
content_list.append(provider_message.ContentElement.from_image_base64(me.base64)) content_list.append(provider_message.ContentElement.from_image_base64(me.base64))
elif isinstance(me, platform_message.File): elif isinstance(me, platform_message.File):
@@ -100,7 +112,9 @@ class PreProcessor(stage.PipelineStage):
if isinstance(msg, platform_message.Plain): if isinstance(msg, platform_message.Plain):
content_list.append(provider_message.ContentElement.from_text(msg.text)) content_list.append(provider_message.ContentElement.from_text(msg.text))
elif isinstance(msg, platform_message.Image): elif isinstance(msg, platform_message.Image):
if selected_runner != 'local-agent' or llm_model.model_entity.abilities.__contains__('vision'): if selected_runner != 'local-agent' or (
llm_model and llm_model.model_entity.abilities.__contains__('vision')
):
if msg.base64 is not None: if msg.base64 is not None:
content_list.append(provider_message.ContentElement.from_image_base64(msg.base64)) content_list.append(provider_message.ContentElement.from_image_base64(msg.base64))

View File

@@ -139,7 +139,7 @@ class WeChatPadMessageConverter(abstract_platform_adapter.AbstractMessageConvert
pattern = r'@\S{1,20}' pattern = r'@\S{1,20}'
content_no_preifx = re.sub(pattern, '', content_no_preifx) content_no_preifx = re.sub(pattern, '', content_no_preifx)
return platform_message.MessageChain([platform_message.Plain(content_no_preifx)]) return platform_message.MessageChain([platform_message.Plain(text=content_no_preifx)])
async def _handler_image(self, message: Optional[dict], content_no_preifx: str) -> platform_message.MessageChain: async def _handler_image(self, message: Optional[dict], content_no_preifx: str) -> platform_message.MessageChain:
"""处理图像消息 (msg_type=3)""" """处理图像消息 (msg_type=3)"""
@@ -265,7 +265,7 @@ class WeChatPadMessageConverter(abstract_platform_adapter.AbstractMessageConvert
# 文本消息 # 文本消息
try: try:
if '<msg>' not in quote_data: if '<msg>' not in quote_data:
quote_data_message_list.append(platform_message.Plain(quote_data)) quote_data_message_list.append(platform_message.Plain(text=quote_data))
else: else:
# 引用消息展开 # 引用消息展开
quote_data_xml = ET.fromstring(quote_data) quote_data_xml = ET.fromstring(quote_data)
@@ -280,7 +280,7 @@ class WeChatPadMessageConverter(abstract_platform_adapter.AbstractMessageConvert
quote_data_message_list.extend(await self._handler_compound(None, quote_data)) quote_data_message_list.extend(await self._handler_compound(None, quote_data))
except Exception as e: except Exception as e:
self.logger.error(f'处理引用消息异常 expcetion:{e}') self.logger.error(f'处理引用消息异常 expcetion:{e}')
quote_data_message_list.append(platform_message.Plain(quote_data)) quote_data_message_list.append(platform_message.Plain(text=quote_data))
message_list.append( message_list.append(
platform_message.Quote( platform_message.Quote(
sender_id=sender_id, sender_id=sender_id,
@@ -290,7 +290,7 @@ class WeChatPadMessageConverter(abstract_platform_adapter.AbstractMessageConvert
if len(user_data) > 0: if len(user_data) > 0:
pattern = r'@\S{1,20}' pattern = r'@\S{1,20}'
user_data = re.sub(pattern, '', user_data) user_data = re.sub(pattern, '', user_data)
message_list.append(platform_message.Plain(user_data)) message_list.append(platform_message.Plain(text=user_data))
return platform_message.MessageChain(message_list) return platform_message.MessageChain(message_list)
@@ -543,7 +543,6 @@ class WeChatPadAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter)
] = {} ] = {}
def __init__(self, config: dict, logger: EventLogger): def __init__(self, config: dict, logger: EventLogger):
quart_app = quart.Quart(__name__) quart_app = quart.Quart(__name__)
message_converter = WeChatPadMessageConverter(config, logger) message_converter = WeChatPadMessageConverter(config, logger)
@@ -551,15 +550,14 @@ class WeChatPadAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter)
bot = WeChatPadClient(config['wechatpad_url'], config['token']) bot = WeChatPadClient(config['wechatpad_url'], config['token'])
super().__init__( super().__init__(
config=config, config=config,
logger = logger, logger=logger,
quart_app = quart_app, quart_app=quart_app,
message_converter =message_converter, message_converter=message_converter,
event_converter = event_converter, event_converter=event_converter,
listeners={}, listeners={},
bot_account_id ='', bot_account_id='',
name="WeChatPad", name='WeChatPad',
bot=bot, bot=bot,
) )
async def ws_message(self, data): async def ws_message(self, data):

View File

@@ -215,7 +215,7 @@ class PluginRuntimeConnector:
if not self.is_enable_plugin: if not self.is_enable_plugin:
return event_ctx return event_ctx
event_ctx_result = await self.handler.emit_event(event_ctx.model_dump(serialize_as_any=True)) event_ctx_result = await self.handler.emit_event(event_ctx.model_dump(serialize_as_any=False))
event_ctx = context.EventContext.model_validate(event_ctx_result['event_context']) event_ctx = context.EventContext.model_validate(event_ctx_result['event_context'])

View File

@@ -1,4 +1,4 @@
semantic_version = 'v4.3.7' semantic_version = 'v4.3.8'
required_database_version = 8 required_database_version = 8
"""Tag the version of the database schema, used to check if the database needs to be migrated""" """Tag the version of the database schema, used to check if the database needs to be migrated"""

View File

@@ -1,6 +1,6 @@
[project] [project]
name = "langbot" name = "langbot"
version = "4.3.7" version = "4.3.8"
description = "Easy-to-use global IM bot platform designed for LLM era" description = "Easy-to-use global IM bot platform designed for LLM era"
readme = "README.md" readme = "README.md"
requires-python = ">=3.10.1,<4.0" requires-python = ">=3.10.1,<4.0"