mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-02 12:05:54 +00:00
* Initial plan * Add package structure and resource path utilities - Created langbot/ package with __init__.py and __main__.py entry point - Added paths utility to find frontend and resource files from package installation - Updated config loading to use resource paths - Updated frontend serving to use resource paths - Added MANIFEST.in for package data inclusion - Updated pyproject.toml with build system and entry points Co-authored-by: RockChinQ <45992437+RockChinQ@users.noreply.github.com> * Add PyPI publishing workflow and update license - Created GitHub Actions workflow to build frontend and publish to PyPI - Added license field to pyproject.toml to fix deprecation warning - Updated .gitignore to exclude build artifacts - Tested package building successfully Co-authored-by: RockChinQ <45992437+RockChinQ@users.noreply.github.com> * Add PyPI installation documentation - Created PYPI_INSTALLATION.md with detailed installation and usage instructions - Updated README.md to feature uvx/pip installation as recommended method - Updated README_EN.md with same changes for English documentation Co-authored-by: RockChinQ <45992437+RockChinQ@users.noreply.github.com> * Address code review feedback - Made package-data configuration more specific to langbot package only - Improved path detection with caching to avoid repeated file I/O - Removed sys.path searching which was incorrect for package data - Removed interactive input() call for non-interactive environment compatibility - Simplified error messages for version check Co-authored-by: RockChinQ <45992437+RockChinQ@users.noreply.github.com> * Fix code review issues - Use specific exception types instead of bare except - Fix misleading comments about directory levels - Remove redundant existence check before makedirs with exist_ok=True - Use context manager for file opening to ensure proper cleanup Co-authored-by: RockChinQ <45992437+RockChinQ@users.noreply.github.com> * Simplify package configuration and document behavioral differences - Removed redundant package-data configuration, relying on MANIFEST.in - Added documentation about behavioral differences between package and source installation - Clarified that include-package-data=true uses MANIFEST.in for data files Co-authored-by: RockChinQ <45992437+RockChinQ@users.noreply.github.com> * chore: update pyproject.toml * chore: try pack templates in langbot/ * chore: update * chore: update * chore: update * chore: update * chore: update * chore: adjust dir structure * chore: fix imports * fix: read default-pipeline-config.json * fix: read default-pipeline-config.json * fix: tests * ci: publish pypi * chore: bump version 4.6.0-beta.1 for testing * chore: add templates/** * fix: send adapters and requesters icons * chore: bump version 4.6.0b2 for testing * chore: add platform field for docker-compose.yaml --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: RockChinQ <45992437+RockChinQ@users.noreply.github.com> Co-authored-by: Junyan Qin <rockchinq@gmail.com>
142 lines
6.4 KiB
Python
142 lines
6.4 KiB
Python
from __future__ import annotations
|
|
|
|
import typing
|
|
|
|
from .. import entities
|
|
from .. import stage
|
|
|
|
import langbot_plugin.api.entities.builtin.platform.message as platform_message
|
|
import langbot_plugin.api.entities.builtin.pipeline.query as pipeline_query
|
|
import langbot_plugin.api.entities.events as events
|
|
|
|
|
|
@stage.stage_class('ResponseWrapper')
|
|
class ResponseWrapper(stage.PipelineStage):
|
|
"""回复包装阶段
|
|
|
|
把回复的 message 包装成人类识读的形式。
|
|
|
|
改写:
|
|
- resp_message_chain
|
|
"""
|
|
|
|
async def initialize(self, pipeline_config: dict):
|
|
pass
|
|
|
|
async def process(
|
|
self,
|
|
query: pipeline_query.Query,
|
|
stage_inst_name: str,
|
|
) -> typing.AsyncGenerator[entities.StageProcessResult, None]:
|
|
"""处理"""
|
|
|
|
# 如果 resp_messages[-1] 已经是 MessageChain 了
|
|
if isinstance(query.resp_messages[-1], platform_message.MessageChain):
|
|
query.resp_message_chain.append(query.resp_messages[-1])
|
|
|
|
yield entities.StageProcessResult(result_type=entities.ResultType.CONTINUE, new_query=query)
|
|
|
|
else:
|
|
if query.resp_messages[-1].role == 'command':
|
|
query.resp_message_chain.append(
|
|
query.resp_messages[-1].get_content_platform_message_chain(prefix_text='[bot] ')
|
|
)
|
|
|
|
yield entities.StageProcessResult(result_type=entities.ResultType.CONTINUE, new_query=query)
|
|
elif query.resp_messages[-1].role == 'plugin':
|
|
query.resp_message_chain.append(query.resp_messages[-1].get_content_platform_message_chain())
|
|
|
|
yield entities.StageProcessResult(result_type=entities.ResultType.CONTINUE, new_query=query)
|
|
else:
|
|
if query.resp_messages[-1].role == 'assistant':
|
|
result = query.resp_messages[-1]
|
|
session = await self.ap.sess_mgr.get_session(query)
|
|
|
|
reply_text = ''
|
|
|
|
if result.content: # 有内容
|
|
reply_text = str(result.get_content_platform_message_chain())
|
|
|
|
# ============= 触发插件事件 ===============
|
|
event = events.NormalMessageResponded(
|
|
launcher_type=query.launcher_type.value,
|
|
launcher_id=query.launcher_id,
|
|
sender_id=query.sender_id,
|
|
session=session,
|
|
prefix='',
|
|
response_text=reply_text,
|
|
finish_reason='stop',
|
|
funcs_called=[fc.function.name for fc in result.tool_calls]
|
|
if result.tool_calls is not None
|
|
else [],
|
|
query=query,
|
|
)
|
|
|
|
# Get bound plugins for filtering
|
|
bound_plugins = query.variables.get('_pipeline_bound_plugins', None)
|
|
event_ctx = await self.ap.plugin_connector.emit_event(event, bound_plugins)
|
|
|
|
if event_ctx.is_prevented_default():
|
|
yield entities.StageProcessResult(
|
|
result_type=entities.ResultType.INTERRUPT,
|
|
new_query=query,
|
|
)
|
|
else:
|
|
if event_ctx.event.reply_message_chain is not None:
|
|
query.resp_message_chain.append(event_ctx.event.reply_message_chain)
|
|
|
|
else:
|
|
query.resp_message_chain.append(result.get_content_platform_message_chain())
|
|
|
|
yield entities.StageProcessResult(
|
|
result_type=entities.ResultType.CONTINUE,
|
|
new_query=query,
|
|
)
|
|
|
|
if result.tool_calls is not None and len(result.tool_calls) > 0: # 有函数调用
|
|
function_names = [tc.function.name for tc in result.tool_calls]
|
|
|
|
reply_text = f'调用函数 {".".join(function_names)}...'
|
|
|
|
query.resp_message_chain.append(
|
|
platform_message.MessageChain([platform_message.Plain(text=reply_text)])
|
|
)
|
|
|
|
if query.pipeline_config['output']['misc']['track-function-calls']:
|
|
event = events.NormalMessageResponded(
|
|
launcher_type=query.launcher_type.value,
|
|
launcher_id=query.launcher_id,
|
|
sender_id=query.sender_id,
|
|
session=session,
|
|
prefix='',
|
|
response_text=reply_text,
|
|
finish_reason='stop',
|
|
funcs_called=[fc.function.name for fc in result.tool_calls]
|
|
if result.tool_calls is not None
|
|
else [],
|
|
query=query,
|
|
)
|
|
|
|
# Get bound plugins for filtering
|
|
bound_plugins = query.variables.get('_pipeline_bound_plugins', None)
|
|
event_ctx = await self.ap.plugin_connector.emit_event(event, bound_plugins)
|
|
|
|
if event_ctx.is_prevented_default():
|
|
yield entities.StageProcessResult(
|
|
result_type=entities.ResultType.INTERRUPT,
|
|
new_query=query,
|
|
)
|
|
else:
|
|
if event_ctx.event.reply_message_chain is not None:
|
|
query.resp_message_chain.append(event_ctx.event.reply_message_chain)
|
|
|
|
else:
|
|
query.resp_message_chain.append(
|
|
platform_message.MessageChain([platform_message.Plain(text=reply_text)])
|
|
)
|
|
|
|
yield entities.StageProcessResult(
|
|
result_type=entities.ResultType.CONTINUE,
|
|
new_query=query,
|
|
)
|