From e642ffa5b31709468bb69d17c17c5eb28ea870a7 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sun, 16 Nov 2025 19:53:01 +0800 Subject: [PATCH] chore: Add PyPI package support for uvx/pip installation (#1764) * 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 --- .github/workflows/publish-to-pypi.yml | 46 ++++++ .gitignore | 6 + README.md | 19 +++ README_EN.md | 19 +++ docker/docker-compose.yaml | 2 + docs/PYPI_INSTALLATION.md | 117 ++++++++++++++ libs/dify_service_api/test.py | 45 ------ main.py | 118 +------------- pkg/utils/announce.py | 115 -------------- pyproject.toml | 17 +- pytest.ini | 2 +- src/langbot/__init__.py | 3 + src/langbot/__main__.py | 104 +++++++++++++ {libs => src/langbot/libs}/LICENSE | 0 {libs => src/langbot/libs}/README.md | 0 .../langbot/libs}/coze_server_api/__init__.py | 0 .../langbot/libs}/coze_server_api/client.py | 93 +++++------ .../langbot/libs}/dify_service_api/README.md | 0 .../libs}/dify_service_api/__init__.py | 0 .../libs}/dify_service_api/v1/__init__.py | 0 .../libs}/dify_service_api/v1/client.py | 0 .../libs}/dify_service_api/v1/client_test.py | 0 .../libs}/dify_service_api/v1/errors.py | 0 .../langbot/libs}/dingtalk_api/EchoHandler.py | 0 .../langbot/libs}/dingtalk_api/__init__.py | 0 .../langbot/libs}/dingtalk_api/api.py | 21 +-- .../libs}/dingtalk_api/dingtalkevent.py | 1 - .../libs}/official_account_api/__init__.py | 0 .../langbot/libs}/official_account_api/api.py | 4 +- .../libs}/official_account_api/oaevent.py | 0 .../langbot/libs}/qq_official_api/__init__.py | 0 .../langbot/libs}/qq_official_api/api.py | 0 .../libs}/qq_official_api/qqofficialevent.py | 0 .../langbot/libs}/slack_api/__init__.py | 0 {libs => src/langbot/libs}/slack_api/api.py | 0 .../langbot/libs}/slack_api/slackevent.py | 0 .../langbot/libs}/wechatpad_api/LICENSE | 0 .../langbot/libs}/wechatpad_api/README.md | 0 .../langbot/libs}/wechatpad_api/__init__.py | 0 .../libs}/wechatpad_api/api/__init__.py | 0 .../libs}/wechatpad_api/api/chatroom.py | 2 +- .../libs}/wechatpad_api/api/downloadpai.py | 2 +- .../langbot/libs}/wechatpad_api/api/friend.py | 0 .../langbot/libs}/wechatpad_api/api/login.py | 2 +- .../libs}/wechatpad_api/api/message.py | 2 +- .../langbot/libs}/wechatpad_api/api/user.py | 2 +- .../langbot/libs}/wechatpad_api/client.py | 12 +- .../libs}/wechatpad_api/util/__init__.py | 0 .../libs}/wechatpad_api/util/http_util.py | 0 .../wechatpad_api/util/terminal_printer.py | 0 .../libs}/wecom_ai_bot_api/WXBizMsgCrypt3.py | 2 +- .../langbot/libs}/wecom_ai_bot_api/api.py | 47 +++--- .../langbot/libs}/wecom_ai_bot_api/ierror.py | 0 .../libs}/wecom_ai_bot_api/wecombotevent.py | 9 +- .../langbot/libs}/wecom_api/WXBizMsgCrypt3.py | 0 .../langbot/libs}/wecom_api/__init__.py | 0 {libs => src/langbot/libs}/wecom_api/api.py | 1 - .../langbot/libs}/wecom_api/ierror.py | 0 .../langbot/libs}/wecom_api/wecomevent.py | 0 .../wecom_customer_service_api/__init__.py | 0 .../libs}/wecom_customer_service_api/api.py | 0 .../wecomcsevent.py | 0 {pkg => src/langbot/pkg}/__init__.py | 0 {pkg => src/langbot/pkg}/api/__init__.py | 0 {pkg => src/langbot/pkg}/api/http/__init__.py | 0 .../pkg}/api/http/controller/__init__.py | 0 .../langbot/pkg}/api/http/controller/group.py | 6 +- .../api/http/controller/groups/__init__.py | 0 .../api/http/controller/groups/apikeys.py | 0 .../pkg}/api/http/controller/groups/files.py | 0 .../controller/groups/knowledge/__init__.py | 0 .../http/controller/groups/knowledge/base.py | 0 .../pkg}/api/http/controller/groups/logs.py | 0 .../controller/groups/pipelines/__init__.py | 0 .../controller/groups/pipelines/pipelines.py | 8 +- .../controller/groups/pipelines/webchat.py | 0 .../controller/groups/platform/__init__.py | 0 .../controller/groups/platform/adapters.py | 7 +- .../http/controller/groups/platform/bots.py | 0 .../api/http/controller/groups/plugins.py | 0 .../controller/groups/provider/__init__.py | 0 .../http/controller/groups/provider/models.py | 0 .../controller/groups/provider/requesters.py | 6 +- .../controller/groups/resources/__init__.py | 0 .../http/controller/groups/resources/mcp.py | 0 .../pkg}/api/http/controller/groups/stats.py | 0 .../pkg}/api/http/controller/groups/system.py | 0 .../pkg}/api/http/controller/groups/user.py | 0 .../api/http/controller/groups/webhooks.py | 0 .../langbot/pkg}/api/http/controller/main.py | 4 +- .../langbot/pkg}/api/http/service/__init__.py | 0 .../langbot/pkg}/api/http/service/apikey.py | 4 +- .../langbot/pkg}/api/http/service/bot.py | 0 .../pkg}/api/http/service/knowledge.py | 0 .../langbot/pkg}/api/http/service/mcp.py | 7 +- .../langbot/pkg}/api/http/service/model.py | 0 .../langbot/pkg}/api/http/service/pipeline.py | 29 ++-- .../langbot/pkg}/api/http/service/user.py | 0 .../langbot/pkg}/api/http/service/webhook.py | 0 {pkg => src/langbot/pkg}/command/__init__.py | 0 {pkg => src/langbot/pkg}/command/cmdmgr.py | 0 {pkg => src/langbot/pkg}/command/operator.py | 0 .../pkg}/command/operators/__init__.py | 0 .../langbot/pkg}/command/operators/delc.py | 0 .../langbot/pkg}/command/operators/last.py | 0 .../langbot/pkg}/command/operators/list.py | 0 .../langbot/pkg}/command/operators/next.py | 0 .../langbot/pkg}/command/operators/prompt.py | 0 .../langbot/pkg}/command/operators/resend.py | 0 {pkg => src/langbot/pkg}/config/__init__.py | 0 .../langbot/pkg}/config/impls/__init__.py | 0 {pkg => src/langbot/pkg}/config/impls/json.py | 29 ++-- .../langbot/pkg}/config/impls/pymodule.py | 0 {pkg => src/langbot/pkg}/config/impls/yaml.py | 29 ++-- {pkg => src/langbot/pkg}/config/manager.py | 12 +- {pkg => src/langbot/pkg}/config/model.py | 0 {pkg => src/langbot/pkg}/core/__init__.py | 0 {pkg => src/langbot/pkg}/core/app.py | 12 +- {pkg => src/langbot/pkg}/core/boot.py | 0 .../langbot/pkg}/core/bootutils/__init__.py | 0 .../langbot/pkg}/core/bootutils/config.py | 0 .../langbot/pkg}/core/bootutils/deps.py | 0 .../langbot/pkg}/core/bootutils/files.py | 5 +- .../langbot/pkg}/core/bootutils/log.py | 0 {pkg => src/langbot/pkg}/core/entities.py | 0 {pkg => src/langbot/pkg}/core/migration.py | 0 .../langbot/pkg}/core/migrations/__init__.py | 0 .../m001_sensitive_word_migration.py | 0 .../m002_openai_config_migration.py | 0 ...m003_anthropic_requester_cfg_completion.py | 0 .../m004_moonshot_cfg_completion.py | 0 .../m005_deepseek_cfg_completion.py | 0 .../core/migrations/m006_vision_config.py | 0 .../core/migrations/m007_qcg_center_url.py | 0 .../m008_ad_fixwin_config_migrate.py | 0 .../core/migrations/m009_msg_truncator_cfg.py | 0 .../m010_ollama_requester_config.py | 0 .../migrations/m011_command_prefix_config.py | 0 .../core/migrations/m012_runner_config.py | 0 .../core/migrations/m013_http_api_config.py | 0 .../migrations/m014_force_delay_config.py | 0 .../core/migrations/m015_gitee_ai_config.py | 0 .../core/migrations/m016_dify_service_api.py | 0 .../m017_dify_api_timeout_params.py | 0 .../pkg}/core/migrations/m018_xai_config.py | 0 .../core/migrations/m019_zhipuai_config.py | 0 .../pkg}/core/migrations/m020_wecom_config.py | 0 .../pkg}/core/migrations/m021_lark_config.py | 0 .../core/migrations/m022_lmstudio_config.py | 0 .../migrations/m023_siliconflow_config.py | 0 .../core/migrations/m024_discord_config.py | 0 .../core/migrations/m025_gewechat_config.py | 0 .../core/migrations/m026_qqofficial_config.py | 0 .../m027_wx_official_account_config.py | 0 .../m028_aliyun_requester_config.py | 0 .../m029_dashscope_app_api_config.py | 0 .../core/migrations/m030_lark_config_cmpl.py | 0 .../core/migrations/m031_dingtalk_config.py | 0 .../core/migrations/m032_volcark_config.py | 0 .../migrations/m033_dify_thinking_config.py | 0 .../m034_gewechat_file_url_config.py | 0 .../pkg}/core/migrations/m035_wxoa_mode.py | 0 .../migrations/m036_wxoa_loading_message.py | 0 .../pkg}/core/migrations/m037_mcp_config.py | 0 .../migrations/m038_tg_dingtalk_markdown.py | 0 .../m039_modelscope_cfg_completion.py | 0 .../pkg}/core/migrations/m040_ppio_config.py | 0 {pkg => src/langbot/pkg}/core/note.py | 0 .../langbot/pkg}/core/notes/__init__.py | 0 .../pkg}/core/notes/n001_classic_msgs.py | 2 - .../notes/n002_selection_mode_on_windows.py | 0 .../pkg}/core/notes/n003_print_version.py | 0 {pkg => src/langbot/pkg}/core/stage.py | 0 .../langbot/pkg}/core/stages/__init__.py | 0 .../langbot/pkg}/core/stages/build_app.py | 8 +- .../langbot/pkg}/core/stages/genkeys.py | 0 .../langbot/pkg}/core/stages/load_config.py | 95 ++++++----- .../langbot/pkg}/core/stages/migrate.py | 0 .../langbot/pkg}/core/stages/setup_logger.py | 0 .../langbot/pkg}/core/stages/show_notes.py | 0 {pkg => src/langbot/pkg}/core/taskmgr.py | 0 {pkg => src/langbot/pkg}/discover/__init__.py | 0 {pkg => src/langbot/pkg}/discover/engine.py | 29 ++-- {pkg => src/langbot/pkg}/entity/__init__.py | 0 .../langbot/pkg}/entity/errors/__init__.py | 0 .../langbot/pkg}/entity/errors/platform.py | 0 .../langbot/pkg}/entity/errors/provider.py | 0 .../pkg}/entity/persistence/__init__.py | 0 .../langbot/pkg}/entity/persistence/apikey.py | 0 .../langbot/pkg}/entity/persistence/base.py | 0 .../langbot/pkg}/entity/persistence/bot.py | 0 .../pkg}/entity/persistence/bstorage.py | 0 .../langbot/pkg}/entity/persistence/mcp.py | 0 .../pkg}/entity/persistence/metadata.py | 0 .../langbot/pkg}/entity/persistence/model.py | 0 .../pkg}/entity/persistence/pipeline.py | 0 .../langbot/pkg}/entity/persistence/plugin.py | 0 .../langbot/pkg}/entity/persistence/rag.py | 0 .../langbot/pkg}/entity/persistence/user.py | 0 .../langbot/pkg}/entity/persistence/vector.py | 0 .../pkg}/entity/persistence/webhook.py | 0 .../langbot/pkg}/entity/rag/__init__.py | 0 .../langbot/pkg}/entity/rag/retriever.py | 0 .../langbot/pkg}/persistence/__init__.py | 0 .../langbot/pkg}/persistence/database.py | 0 .../pkg}/persistence/databases/__init__.py | 0 .../pkg}/persistence/databases/postgresql.py | 0 .../pkg}/persistence/databases/sqlite.py | 0 {pkg => src/langbot/pkg}/persistence/mgr.py | 2 +- .../langbot/pkg}/persistence/migration.py | 0 .../pkg}/persistence/migrations/__init__.py | 0 .../migrations/dbm001_migrate_v3_config.py | 4 +- .../dbm002_combine_quote_msg_config.py | 0 .../migrations/dbm003_n8n_config.py | 0 .../migrations/dbm004_rag_kb_uuid.py | 0 .../dbm005_pipeline_remove_cot_config.py | 0 .../migrations/dbm006_langflow_api_config.py | 0 .../dbm007_plugin_install_source.py | 0 .../migrations/dbm008_plugin_config.py | 0 .../dbm009_pipeline_extension_preferences.py | 0 .../dbm010_pipeline_multi_knowledge_base.py | 12 +- .../dbm011_dify_base_prompt_config.py | 0 {pkg => src/langbot/pkg}/pipeline/__init__.py | 0 .../langbot/pkg}/pipeline/bansess/__init__.py | 0 .../langbot/pkg}/pipeline/bansess/bansess.py | 0 .../pkg}/pipeline/cntfilter/__init__.py | 0 .../pkg}/pipeline/cntfilter/cntfilter.py | 0 .../pkg}/pipeline/cntfilter/entities.py | 0 .../langbot/pkg}/pipeline/cntfilter/filter.py | 0 .../pipeline/cntfilter/filters/__init__.py | 0 .../cntfilter/filters/baiduexamine.py | 0 .../pipeline/cntfilter/filters/banwords.py | 0 .../pipeline/cntfilter/filters/cntignore.py | 0 .../langbot/pkg}/pipeline/controller.py | 0 {pkg => src/langbot/pkg}/pipeline/entities.py | 0 .../pkg}/pipeline/longtext/__init__.py | 0 .../pkg}/pipeline/longtext/longtext.py | 0 .../pipeline/longtext/strategies/__init__.py | 0 .../pipeline/longtext/strategies/forward.py | 0 .../pipeline/longtext/strategies/image.py | 0 .../pkg}/pipeline/longtext/strategy.py | 0 .../langbot/pkg}/pipeline/msgtrun/__init__.py | 0 .../langbot/pkg}/pipeline/msgtrun/msgtrun.py | 0 .../pkg}/pipeline/msgtrun/truncator.py | 0 .../pipeline/msgtrun/truncators/__init__.py | 0 .../pkg}/pipeline/msgtrun/truncators/round.py | 0 .../langbot/pkg}/pipeline/pipelinemgr.py | 12 +- {pkg => src/langbot/pkg}/pipeline/pool.py | 0 .../langbot/pkg}/pipeline/preproc/__init__.py | 0 .../langbot/pkg}/pipeline/preproc/preproc.py | 0 .../langbot/pkg}/pipeline/process/__init__.py | 0 .../langbot/pkg}/pipeline/process/handler.py | 0 .../pipeline/process/handlers/__init__.py | 0 .../pkg}/pipeline/process/handlers/chat.py | 0 .../pkg}/pipeline/process/handlers/command.py | 0 .../langbot/pkg}/pipeline/process/process.py | 0 .../pkg}/pipeline/ratelimit/__init__.py | 0 .../langbot/pkg}/pipeline/ratelimit/algo.py | 0 .../pkg}/pipeline/ratelimit/algos/__init__.py | 0 .../pkg}/pipeline/ratelimit/algos/fixedwin.py | 0 .../pkg}/pipeline/ratelimit/ratelimit.py | 0 .../pkg}/pipeline/respback/__init__.py | 0 .../pkg}/pipeline/respback/respback.py | 0 .../pkg}/pipeline/resprule/__init__.py | 0 .../pkg}/pipeline/resprule/entities.py | 0 .../pkg}/pipeline/resprule/resprule.py | 0 .../langbot/pkg}/pipeline/resprule/rule.py | 0 .../pkg}/pipeline/resprule/rules/__init__.py | 0 .../pkg}/pipeline/resprule/rules/atbot.py | 4 +- .../pkg}/pipeline/resprule/rules/prefix.py | 0 .../pkg}/pipeline/resprule/rules/random.py | 0 .../pkg}/pipeline/resprule/rules/regexp.py | 0 {pkg => src/langbot/pkg}/pipeline/stage.py | 0 .../langbot/pkg}/pipeline/wrapper/__init__.py | 0 .../langbot/pkg}/pipeline/wrapper/wrapper.py | 0 {pkg => src/langbot/pkg}/platform/__init__.py | 0 {pkg => src/langbot/pkg}/platform/botmgr.py | 9 +- {pkg => src/langbot/pkg}/platform/logger.py | 0 .../langbot/pkg}/platform/sources/__init__.py | 0 .../pkg}/platform/sources/aiocqhttp.py | 0 .../pkg}/platform/sources/aiocqhttp.yaml | 0 .../langbot/pkg}/platform/sources/dingtalk.py | 27 ++-- .../pkg}/platform/sources/dingtalk.svg | 0 .../pkg}/platform/sources/dingtalk.yaml | 0 .../langbot/pkg}/platform/sources/discord.py | 1 - .../langbot/pkg}/platform/sources/discord.svg | 0 .../pkg}/platform/sources/discord.yaml | 0 .../langbot/pkg}/platform/sources/lark.py | 1 + .../langbot/pkg}/platform/sources/lark.svg | 0 .../langbot/pkg}/platform/sources/lark.yaml | 0 .../pkg}/platform/sources/legacy/gewechat.png | Bin .../pkg}/platform/sources/legacy/gewechat.py | 0 .../platform/sources/legacy/gewechat.yaml | 0 .../pkg}/platform/sources/legacy/nakuru.png | Bin .../pkg}/platform/sources/legacy/nakuru.py | 0 .../pkg}/platform/sources/legacy/nakuru.yaml | 0 .../pkg}/platform/sources/legacy/qqbotpy.py | 0 .../pkg}/platform/sources/legacy/qqbotpy.svg | 0 .../pkg}/platform/sources/legacy/qqbotpy.yaml | 0 .../langbot/pkg}/platform/sources/line.png | Bin .../langbot/pkg}/platform/sources/line.py | 106 +++++-------- .../langbot/pkg}/platform/sources/line.yaml | 0 .../pkg}/platform/sources/officialaccount.png | Bin .../pkg}/platform/sources/officialaccount.py | 20 +-- .../platform/sources/officialaccount.yaml | 0 .../langbot/pkg}/platform/sources/onebot.png | Bin .../pkg}/platform/sources/qqofficial.py | 13 +- .../pkg}/platform/sources/qqofficial.svg | 0 .../pkg}/platform/sources/qqofficial.yaml | 0 .../langbot/pkg}/platform/sources/slack.png | Bin .../langbot/pkg}/platform/sources/slack.py | 8 +- .../langbot/pkg}/platform/sources/slack.yaml | 0 .../langbot/pkg}/platform/sources/telegram.py | 0 .../pkg}/platform/sources/telegram.svg | 0 .../pkg}/platform/sources/telegram.yaml | 0 .../langbot/pkg}/platform/sources/webchat.py | 0 .../pkg}/platform/sources/webchat.yaml | 0 .../pkg}/platform/sources/wechatpad.png | Bin .../pkg}/platform/sources/wechatpad.py | 4 +- .../pkg}/platform/sources/wechatpad.yaml | 0 .../langbot/pkg}/platform/sources/wecom.png | Bin .../langbot/pkg}/platform/sources/wecom.py | 15 +- .../langbot/pkg}/platform/sources/wecom.yaml | 0 .../pkg}/platform/sources/wecombot.png | Bin .../langbot/pkg}/platform/sources/wecombot.py | 43 ++--- .../pkg}/platform/sources/wecombot.yaml | 0 .../langbot/pkg}/platform/sources/wecomcs.py | 4 +- .../pkg}/platform/sources/wecomcs.yaml | 0 .../langbot/pkg}/platform/webhook_pusher.py | 0 {pkg => src/langbot/pkg}/plugin/__init__.py | 0 {pkg => src/langbot/pkg}/plugin/connector.py | 12 +- {pkg => src/langbot/pkg}/plugin/handler.py | 0 {pkg => src/langbot/pkg}/provider/__init__.py | 0 .../pkg}/provider/modelmgr/__init__.py | 0 .../pkg}/provider/modelmgr/entities.py | 0 .../langbot/pkg}/provider/modelmgr/errors.py | 0 .../pkg}/provider/modelmgr/modelmgr.py | 0 .../pkg}/provider/modelmgr/requester.py | 0 .../pkg}/provider/modelmgr/requester.yaml | 0 .../provider/modelmgr/requesters/302ai.png | Bin .../modelmgr/requesters/302aichatcmpl.py | 0 .../modelmgr/requesters/302aichatcmpl.yaml | 0 .../provider/modelmgr/requesters/__init__.py | 0 .../modelmgr/requesters/anthropic.svg | 0 .../modelmgr/requesters/anthropicmsgs.py | 0 .../modelmgr/requesters/anthropicmsgs.yaml | 0 .../provider/modelmgr/requesters/bailian.png | Bin .../modelmgr/requesters/bailianchatcmpl.py | 46 ++++-- .../modelmgr/requesters/bailianchatcmpl.yaml | 0 .../provider/modelmgr/requesters/chatcmpl.py | 0 .../modelmgr/requesters/chatcmpl.yaml | 0 .../modelmgr/requesters/compshare.png | Bin .../modelmgr/requesters/compsharechatcmpl.py | 0 .../requesters/compsharechatcmpl.yaml | 0 .../provider/modelmgr/requesters/deepseek.svg | 0 .../modelmgr/requesters/deepseekchatcmpl.py | 0 .../modelmgr/requesters/deepseekchatcmpl.yaml | 0 .../provider/modelmgr/requesters/gemini.svg | 0 .../modelmgr/requesters/geminichatcmpl.py | 0 .../modelmgr/requesters/geminichatcmpl.yaml | 0 .../provider/modelmgr/requesters/giteeai.svg | 0 .../modelmgr/requesters/giteeaichatcmpl.py | 0 .../modelmgr/requesters/giteeaichatcmpl.yaml | 0 .../provider/modelmgr/requesters/jiekouai.png | Bin .../modelmgr/requesters/jiekouaichatcmpl.py | 0 .../modelmgr/requesters/jiekouaichatcmpl.yaml | 0 .../modelmgr/requesters/lmstudio.webp | Bin .../modelmgr/requesters/lmstudiochatcmpl.py | 0 .../modelmgr/requesters/lmstudiochatcmpl.yaml | 0 .../modelmgr/requesters/modelscope.svg | 0 .../modelmgr/requesters/modelscopechatcmpl.py | 0 .../requesters/modelscopechatcmpl.yaml | 0 .../provider/modelmgr/requesters/moonshot.png | Bin .../modelmgr/requesters/moonshotchatcmpl.py | 0 .../modelmgr/requesters/moonshotchatcmpl.yaml | 0 .../provider/modelmgr/requesters/newapi.png | Bin .../modelmgr/requesters/newapichatcmpl.py | 0 .../modelmgr/requesters/newapichatcmpl.yaml | 0 .../provider/modelmgr/requesters/ollama.svg | 0 .../modelmgr/requesters/ollamachat.py | 0 .../modelmgr/requesters/ollamachat.yaml | 0 .../provider/modelmgr/requesters/openai.svg | 0 .../modelmgr/requesters/openrouter.svg | 0 .../modelmgr/requesters/openrouterchatcmpl.py | 0 .../requesters/openrouterchatcmpl.yaml | 0 .../provider/modelmgr/requesters/ppio.svg | 0 .../modelmgr/requesters/ppiochatcmpl.py | 0 .../modelmgr/requesters/ppiochatcmpl.yaml | 0 .../provider/modelmgr/requesters/qhaigc.png | Bin .../modelmgr/requesters/qhaigcchatcmpl.py | 0 .../modelmgr/requesters/qhaigcchatcmpl.yaml | 0 .../modelmgr/requesters/shengsuanyun.py | 0 .../modelmgr/requesters/shengsuanyun.svg | 0 .../modelmgr/requesters/shengsuanyun.yaml | 0 .../modelmgr/requesters/siliconflow.svg | 0 .../requesters/siliconflowchatcmpl.py | 0 .../requesters/siliconflowchatcmpl.yaml | 0 .../modelmgr/requesters/tokenpony.svg | 0 .../modelmgr/requesters/tokenpony.yaml | 0 .../modelmgr/requesters/tokenponychatcmpl.py | 0 .../provider/modelmgr/requesters/volcark.svg | 0 .../modelmgr/requesters/volcarkchatcmpl.py | 0 .../modelmgr/requesters/volcarkchatcmpl.yaml | 0 .../pkg}/provider/modelmgr/requesters/xai.svg | 0 .../modelmgr/requesters/xaichatcmpl.py | 0 .../modelmgr/requesters/xaichatcmpl.yaml | 0 .../provider/modelmgr/requesters/zhipuai.svg | 0 .../modelmgr/requesters/zhipuaichatcmpl.py | 0 .../modelmgr/requesters/zhipuaichatcmpl.yaml | 0 .../langbot/pkg}/provider/modelmgr/token.py | 0 {pkg => src/langbot/pkg}/provider/runner.py | 8 +- .../langbot/pkg}/provider/runners/__init__.py | 0 .../langbot/pkg}/provider/runners/cozeapi.py | 147 ++++++++---------- .../pkg}/provider/runners/dashscopeapi.py | 0 .../pkg}/provider/runners/difysvapi.py | 9 +- .../pkg}/provider/runners/langflowapi.py | 0 .../pkg}/provider/runners/localagent.py | 6 +- .../langbot/pkg}/provider/runners/n8nsvapi.py | 0 .../langbot/pkg}/provider/runners/tboxapi.py | 23 ++- .../langbot/pkg}/provider/session/__init__.py | 0 .../pkg}/provider/session/sessionmgr.py | 0 .../langbot/pkg}/provider/tools/__init__.py | 0 .../langbot/pkg}/provider/tools/loader.py | 0 .../pkg}/provider/tools/loaders/__init__.py | 0 .../pkg}/provider/tools/loaders/mcp.py | 0 .../pkg}/provider/tools/loaders/plugin.py | 0 .../langbot/pkg}/provider/tools/toolmgr.py | 10 +- .../langbot/pkg}/rag/knowledge/kbmgr.py | 12 +- .../pkg}/rag/knowledge/services/__init__.py | 0 .../rag/knowledge/services/base_service.py | 0 .../pkg}/rag/knowledge/services/chunker.py | 4 +- .../pkg}/rag/knowledge/services/embedder.py | 8 +- .../pkg}/rag/knowledge/services/parser.py | 2 +- .../pkg}/rag/knowledge/services/retriever.py | 0 {pkg => src/langbot/pkg}/storage/__init__.py | 0 {pkg => src/langbot/pkg}/storage/mgr.py | 0 {pkg => src/langbot/pkg}/storage/provider.py | 0 .../pkg}/storage/providers/__init__.py | 0 .../pkg}/storage/providers/localstorage.py | 0 .../pkg}/storage/providers/s3storage.py | 0 {pkg => src/langbot/pkg}/utils/__init__.py | 0 {pkg => src/langbot/pkg}/utils/constants.py | 2 +- {pkg => src/langbot/pkg}/utils/funcschema.py | 0 {pkg => src/langbot/pkg}/utils/image.py | 0 {pkg => src/langbot/pkg}/utils/importutil.py | 19 ++- {pkg => src/langbot/pkg}/utils/logcache.py | 0 src/langbot/pkg/utils/paths.py | 92 +++++++++++ {pkg => src/langbot/pkg}/utils/pkgmgr.py | 0 {pkg => src/langbot/pkg}/utils/platform.py | 0 {pkg => src/langbot/pkg}/utils/proxy.py | 0 {pkg => src/langbot/pkg}/utils/version.py | 2 +- {pkg => src/langbot/pkg}/vector/__init__.py | 0 {pkg => src/langbot/pkg}/vector/mgr.py | 0 {pkg => src/langbot/pkg}/vector/vdb.py | 0 .../langbot/pkg}/vector/vdbs/__init__.py | 0 .../langbot/pkg}/vector/vdbs/chroma.py | 4 +- .../langbot/pkg}/vector/vdbs/qdrant.py | 4 +- .../langbot/templates}/__init__.py | 0 .../langbot/templates/components.yaml | 3 - .../langbot/templates}/config.yaml | 0 .../templates}/default-pipeline-config.json | 0 .../langbot/templates}/legacy/command.json | 0 .../langbot/templates}/legacy/pipeline.json | 0 .../langbot/templates}/legacy/platform.json | 0 .../langbot/templates}/legacy/provider.json | 0 .../langbot/templates}/legacy/system.json | 0 .../templates}/metadata/pipeline/ai.yaml | 0 .../templates}/metadata/pipeline/output.yaml | 0 .../templates}/metadata/pipeline/safety.yaml | 0 .../templates}/metadata/pipeline/trigger.yaml | 0 .../templates}/metadata/sensitive-words.json | 0 tests/unit_tests/pipeline/conftest.py | 9 +- tests/unit_tests/pipeline/test_bansess.py | 69 ++------ tests/unit_tests/pipeline/test_pipelinemgr.py | 8 +- tests/unit_tests/pipeline/test_ratelimit.py | 19 +-- tests/unit_tests/pipeline/test_resprule.py | 73 +++------ .../test_storage_provider_selection.py | 22 +-- 477 files changed, 1001 insertions(+), 1002 deletions(-) create mode 100644 .github/workflows/publish-to-pypi.yml create mode 100644 docs/PYPI_INSTALLATION.md delete mode 100644 libs/dify_service_api/test.py delete mode 100644 pkg/utils/announce.py create mode 100644 src/langbot/__init__.py create mode 100644 src/langbot/__main__.py rename {libs => src/langbot/libs}/LICENSE (100%) rename {libs => src/langbot/libs}/README.md (100%) rename {libs => src/langbot/libs}/coze_server_api/__init__.py (100%) rename {libs => src/langbot/libs}/coze_server_api/client.py (60%) rename {libs => src/langbot/libs}/dify_service_api/README.md (100%) rename {libs => src/langbot/libs}/dify_service_api/__init__.py (100%) rename {libs => src/langbot/libs}/dify_service_api/v1/__init__.py (100%) rename {libs => src/langbot/libs}/dify_service_api/v1/client.py (100%) rename {libs => src/langbot/libs}/dify_service_api/v1/client_test.py (100%) rename {libs => src/langbot/libs}/dify_service_api/v1/errors.py (100%) rename {libs => src/langbot/libs}/dingtalk_api/EchoHandler.py (100%) rename {libs => src/langbot/libs}/dingtalk_api/__init__.py (100%) rename {libs => src/langbot/libs}/dingtalk_api/api.py (96%) rename {libs => src/langbot/libs}/dingtalk_api/dingtalkevent.py (99%) rename {libs => src/langbot/libs}/official_account_api/__init__.py (100%) rename {libs => src/langbot/libs}/official_account_api/api.py (99%) rename {libs => src/langbot/libs}/official_account_api/oaevent.py (100%) rename {libs => src/langbot/libs}/qq_official_api/__init__.py (100%) rename {libs => src/langbot/libs}/qq_official_api/api.py (100%) rename {libs => src/langbot/libs}/qq_official_api/qqofficialevent.py (100%) rename {libs => src/langbot/libs}/slack_api/__init__.py (100%) rename {libs => src/langbot/libs}/slack_api/api.py (100%) rename {libs => src/langbot/libs}/slack_api/slackevent.py (100%) rename {libs => src/langbot/libs}/wechatpad_api/LICENSE (100%) rename {libs => src/langbot/libs}/wechatpad_api/README.md (100%) rename {libs => src/langbot/libs}/wechatpad_api/__init__.py (100%) rename {libs => src/langbot/libs}/wechatpad_api/api/__init__.py (100%) rename {libs => src/langbot/libs}/wechatpad_api/api/chatroom.py (84%) rename {libs => src/langbot/libs}/wechatpad_api/api/downloadpai.py (94%) rename {libs => src/langbot/libs}/wechatpad_api/api/friend.py (100%) rename {libs => src/langbot/libs}/wechatpad_api/api/login.py (96%) rename {libs => src/langbot/libs}/wechatpad_api/api/message.py (97%) rename {libs => src/langbot/libs}/wechatpad_api/api/user.py (91%) rename {libs => src/langbot/libs}/wechatpad_api/client.py (89%) rename {libs => src/langbot/libs}/wechatpad_api/util/__init__.py (100%) rename {libs => src/langbot/libs}/wechatpad_api/util/http_util.py (100%) rename {libs => src/langbot/libs}/wechatpad_api/util/terminal_printer.py (100%) rename {libs => src/langbot/libs}/wecom_ai_bot_api/WXBizMsgCrypt3.py (99%) rename {libs => src/langbot/libs}/wecom_ai_bot_api/api.py (93%) rename {libs => src/langbot/libs}/wecom_ai_bot_api/ierror.py (100%) rename {libs => src/langbot/libs}/wecom_ai_bot_api/wecombotevent.py (88%) rename {libs => src/langbot/libs}/wecom_api/WXBizMsgCrypt3.py (100%) rename {libs => src/langbot/libs}/wecom_api/__init__.py (100%) rename {libs => src/langbot/libs}/wecom_api/api.py (99%) rename {libs => src/langbot/libs}/wecom_api/ierror.py (100%) rename {libs => src/langbot/libs}/wecom_api/wecomevent.py (100%) rename {libs => src/langbot/libs}/wecom_customer_service_api/__init__.py (100%) rename {libs => src/langbot/libs}/wecom_customer_service_api/api.py (100%) rename {libs => src/langbot/libs}/wecom_customer_service_api/wecomcsevent.py (100%) rename {pkg => src/langbot/pkg}/__init__.py (100%) rename {pkg => src/langbot/pkg}/api/__init__.py (100%) rename {pkg => src/langbot/pkg}/api/http/__init__.py (100%) rename {pkg => src/langbot/pkg}/api/http/controller/__init__.py (100%) rename {pkg => src/langbot/pkg}/api/http/controller/group.py (97%) rename {pkg => src/langbot/pkg}/api/http/controller/groups/__init__.py (100%) rename {pkg => src/langbot/pkg}/api/http/controller/groups/apikeys.py (100%) rename {pkg => src/langbot/pkg}/api/http/controller/groups/files.py (100%) rename {pkg => src/langbot/pkg}/api/http/controller/groups/knowledge/__init__.py (100%) rename {pkg => src/langbot/pkg}/api/http/controller/groups/knowledge/base.py (100%) rename {pkg => src/langbot/pkg}/api/http/controller/groups/logs.py (100%) rename {pkg => src/langbot/pkg}/api/http/controller/groups/pipelines/__init__.py (100%) rename {pkg => src/langbot/pkg}/api/http/controller/groups/pipelines/pipelines.py (92%) rename {pkg => src/langbot/pkg}/api/http/controller/groups/pipelines/webchat.py (100%) rename {pkg => src/langbot/pkg}/api/http/controller/groups/platform/__init__.py (100%) rename {pkg => src/langbot/pkg}/api/http/controller/groups/platform/adapters.py (85%) rename {pkg => src/langbot/pkg}/api/http/controller/groups/platform/bots.py (100%) rename {pkg => src/langbot/pkg}/api/http/controller/groups/plugins.py (100%) rename {pkg => src/langbot/pkg}/api/http/controller/groups/provider/__init__.py (100%) rename {pkg => src/langbot/pkg}/api/http/controller/groups/provider/models.py (100%) rename {pkg => src/langbot/pkg}/api/http/controller/groups/provider/requesters.py (86%) rename {pkg => src/langbot/pkg}/api/http/controller/groups/resources/__init__.py (100%) rename {pkg => src/langbot/pkg}/api/http/controller/groups/resources/mcp.py (100%) rename {pkg => src/langbot/pkg}/api/http/controller/groups/stats.py (100%) rename {pkg => src/langbot/pkg}/api/http/controller/groups/system.py (100%) rename {pkg => src/langbot/pkg}/api/http/controller/groups/user.py (100%) rename {pkg => src/langbot/pkg}/api/http/controller/groups/webhooks.py (100%) rename {pkg => src/langbot/pkg}/api/http/controller/main.py (98%) rename {pkg => src/langbot/pkg}/api/http/service/__init__.py (100%) rename {pkg => src/langbot/pkg}/api/http/service/apikey.py (94%) rename {pkg => src/langbot/pkg}/api/http/service/bot.py (100%) rename {pkg => src/langbot/pkg}/api/http/service/knowledge.py (100%) rename {pkg => src/langbot/pkg}/api/http/service/mcp.py (98%) rename {pkg => src/langbot/pkg}/api/http/service/model.py (100%) rename {pkg => src/langbot/pkg}/api/http/service/pipeline.py (90%) rename {pkg => src/langbot/pkg}/api/http/service/user.py (100%) rename {pkg => src/langbot/pkg}/api/http/service/webhook.py (100%) rename {pkg => src/langbot/pkg}/command/__init__.py (100%) rename {pkg => src/langbot/pkg}/command/cmdmgr.py (100%) rename {pkg => src/langbot/pkg}/command/operator.py (100%) rename {pkg => src/langbot/pkg}/command/operators/__init__.py (100%) rename {pkg => src/langbot/pkg}/command/operators/delc.py (100%) rename {pkg => src/langbot/pkg}/command/operators/last.py (100%) rename {pkg => src/langbot/pkg}/command/operators/list.py (100%) rename {pkg => src/langbot/pkg}/command/operators/next.py (100%) rename {pkg => src/langbot/pkg}/command/operators/prompt.py (100%) rename {pkg => src/langbot/pkg}/command/operators/resend.py (100%) rename {pkg => src/langbot/pkg}/config/__init__.py (100%) rename {pkg => src/langbot/pkg}/config/impls/__init__.py (100%) rename {pkg => src/langbot/pkg}/config/impls/json.py (65%) rename {pkg => src/langbot/pkg}/config/impls/pymodule.py (100%) rename {pkg => src/langbot/pkg}/config/impls/yaml.py (64%) rename {pkg => src/langbot/pkg}/config/manager.py (86%) rename {pkg => src/langbot/pkg}/config/model.py (100%) rename {pkg => src/langbot/pkg}/core/__init__.py (100%) rename {pkg => src/langbot/pkg}/core/app.py (96%) rename {pkg => src/langbot/pkg}/core/boot.py (100%) rename {pkg => src/langbot/pkg}/core/bootutils/__init__.py (100%) rename {pkg => src/langbot/pkg}/core/bootutils/config.py (100%) rename {pkg => src/langbot/pkg}/core/bootutils/deps.py (100%) rename {pkg => src/langbot/pkg}/core/bootutils/files.py (77%) rename {pkg => src/langbot/pkg}/core/bootutils/log.py (100%) rename {pkg => src/langbot/pkg}/core/entities.py (100%) rename {pkg => src/langbot/pkg}/core/migration.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/__init__.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m001_sensitive_word_migration.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m002_openai_config_migration.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m003_anthropic_requester_cfg_completion.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m004_moonshot_cfg_completion.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m005_deepseek_cfg_completion.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m006_vision_config.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m007_qcg_center_url.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m008_ad_fixwin_config_migrate.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m009_msg_truncator_cfg.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m010_ollama_requester_config.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m011_command_prefix_config.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m012_runner_config.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m013_http_api_config.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m014_force_delay_config.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m015_gitee_ai_config.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m016_dify_service_api.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m017_dify_api_timeout_params.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m018_xai_config.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m019_zhipuai_config.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m020_wecom_config.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m021_lark_config.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m022_lmstudio_config.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m023_siliconflow_config.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m024_discord_config.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m025_gewechat_config.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m026_qqofficial_config.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m027_wx_official_account_config.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m028_aliyun_requester_config.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m029_dashscope_app_api_config.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m030_lark_config_cmpl.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m031_dingtalk_config.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m032_volcark_config.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m033_dify_thinking_config.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m034_gewechat_file_url_config.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m035_wxoa_mode.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m036_wxoa_loading_message.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m037_mcp_config.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m038_tg_dingtalk_markdown.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m039_modelscope_cfg_completion.py (100%) rename {pkg => src/langbot/pkg}/core/migrations/m040_ppio_config.py (100%) rename {pkg => src/langbot/pkg}/core/note.py (100%) rename {pkg => src/langbot/pkg}/core/notes/__init__.py (100%) rename {pkg => src/langbot/pkg}/core/notes/n001_classic_msgs.py (87%) rename {pkg => src/langbot/pkg}/core/notes/n002_selection_mode_on_windows.py (100%) rename {pkg => src/langbot/pkg}/core/notes/n003_print_version.py (100%) rename {pkg => src/langbot/pkg}/core/stage.py (100%) rename {pkg => src/langbot/pkg}/core/stages/__init__.py (100%) rename {pkg => src/langbot/pkg}/core/stages/build_app.py (96%) rename {pkg => src/langbot/pkg}/core/stages/genkeys.py (100%) rename {pkg => src/langbot/pkg}/core/stages/load_config.py (60%) rename {pkg => src/langbot/pkg}/core/stages/migrate.py (100%) rename {pkg => src/langbot/pkg}/core/stages/setup_logger.py (100%) rename {pkg => src/langbot/pkg}/core/stages/show_notes.py (100%) rename {pkg => src/langbot/pkg}/core/taskmgr.py (100%) rename {pkg => src/langbot/pkg}/discover/__init__.py (100%) rename {pkg => src/langbot/pkg}/discover/engine.py (91%) rename {pkg => src/langbot/pkg}/entity/__init__.py (100%) rename {pkg => src/langbot/pkg}/entity/errors/__init__.py (100%) rename {pkg => src/langbot/pkg}/entity/errors/platform.py (100%) rename {pkg => src/langbot/pkg}/entity/errors/provider.py (100%) rename {pkg => src/langbot/pkg}/entity/persistence/__init__.py (100%) rename {pkg => src/langbot/pkg}/entity/persistence/apikey.py (100%) rename {pkg => src/langbot/pkg}/entity/persistence/base.py (100%) rename {pkg => src/langbot/pkg}/entity/persistence/bot.py (100%) rename {pkg => src/langbot/pkg}/entity/persistence/bstorage.py (100%) rename {pkg => src/langbot/pkg}/entity/persistence/mcp.py (100%) rename {pkg => src/langbot/pkg}/entity/persistence/metadata.py (100%) rename {pkg => src/langbot/pkg}/entity/persistence/model.py (100%) rename {pkg => src/langbot/pkg}/entity/persistence/pipeline.py (100%) rename {pkg => src/langbot/pkg}/entity/persistence/plugin.py (100%) rename {pkg => src/langbot/pkg}/entity/persistence/rag.py (100%) rename {pkg => src/langbot/pkg}/entity/persistence/user.py (100%) rename {pkg => src/langbot/pkg}/entity/persistence/vector.py (100%) rename {pkg => src/langbot/pkg}/entity/persistence/webhook.py (100%) rename {pkg => src/langbot/pkg}/entity/rag/__init__.py (100%) rename {pkg => src/langbot/pkg}/entity/rag/retriever.py (100%) rename {pkg => src/langbot/pkg}/persistence/__init__.py (100%) rename {pkg => src/langbot/pkg}/persistence/database.py (100%) rename {pkg => src/langbot/pkg}/persistence/databases/__init__.py (100%) rename {pkg => src/langbot/pkg}/persistence/databases/postgresql.py (100%) rename {pkg => src/langbot/pkg}/persistence/databases/sqlite.py (100%) rename {pkg => src/langbot/pkg}/persistence/mgr.py (97%) rename {pkg => src/langbot/pkg}/persistence/migration.py (100%) rename {pkg => src/langbot/pkg}/persistence/migrations/__init__.py (100%) rename {pkg => src/langbot/pkg}/persistence/migrations/dbm001_migrate_v3_config.py (99%) rename {pkg => src/langbot/pkg}/persistence/migrations/dbm002_combine_quote_msg_config.py (100%) rename {pkg => src/langbot/pkg}/persistence/migrations/dbm003_n8n_config.py (100%) rename {pkg => src/langbot/pkg}/persistence/migrations/dbm004_rag_kb_uuid.py (100%) rename {pkg => src/langbot/pkg}/persistence/migrations/dbm005_pipeline_remove_cot_config.py (100%) rename {pkg => src/langbot/pkg}/persistence/migrations/dbm006_langflow_api_config.py (100%) rename {pkg => src/langbot/pkg}/persistence/migrations/dbm007_plugin_install_source.py (100%) rename {pkg => src/langbot/pkg}/persistence/migrations/dbm008_plugin_config.py (100%) rename {pkg => src/langbot/pkg}/persistence/migrations/dbm009_pipeline_extension_preferences.py (100%) rename {pkg => src/langbot/pkg}/persistence/migrations/dbm010_pipeline_multi_knowledge_base.py (97%) rename {pkg => src/langbot/pkg}/persistence/migrations/dbm011_dify_base_prompt_config.py (100%) rename {pkg => src/langbot/pkg}/pipeline/__init__.py (100%) rename {pkg => src/langbot/pkg}/pipeline/bansess/__init__.py (100%) rename {pkg => src/langbot/pkg}/pipeline/bansess/bansess.py (100%) rename {pkg => src/langbot/pkg}/pipeline/cntfilter/__init__.py (100%) rename {pkg => src/langbot/pkg}/pipeline/cntfilter/cntfilter.py (100%) rename {pkg => src/langbot/pkg}/pipeline/cntfilter/entities.py (100%) rename {pkg => src/langbot/pkg}/pipeline/cntfilter/filter.py (100%) rename {pkg => src/langbot/pkg}/pipeline/cntfilter/filters/__init__.py (100%) rename {pkg => src/langbot/pkg}/pipeline/cntfilter/filters/baiduexamine.py (100%) rename {pkg => src/langbot/pkg}/pipeline/cntfilter/filters/banwords.py (100%) rename {pkg => src/langbot/pkg}/pipeline/cntfilter/filters/cntignore.py (100%) rename {pkg => src/langbot/pkg}/pipeline/controller.py (100%) rename {pkg => src/langbot/pkg}/pipeline/entities.py (100%) rename {pkg => src/langbot/pkg}/pipeline/longtext/__init__.py (100%) rename {pkg => src/langbot/pkg}/pipeline/longtext/longtext.py (100%) rename {pkg => src/langbot/pkg}/pipeline/longtext/strategies/__init__.py (100%) rename {pkg => src/langbot/pkg}/pipeline/longtext/strategies/forward.py (100%) rename {pkg => src/langbot/pkg}/pipeline/longtext/strategies/image.py (100%) rename {pkg => src/langbot/pkg}/pipeline/longtext/strategy.py (100%) rename {pkg => src/langbot/pkg}/pipeline/msgtrun/__init__.py (100%) rename {pkg => src/langbot/pkg}/pipeline/msgtrun/msgtrun.py (100%) rename {pkg => src/langbot/pkg}/pipeline/msgtrun/truncator.py (100%) rename {pkg => src/langbot/pkg}/pipeline/msgtrun/truncators/__init__.py (100%) rename {pkg => src/langbot/pkg}/pipeline/msgtrun/truncators/round.py (100%) rename {pkg => src/langbot/pkg}/pipeline/pipelinemgr.py (99%) rename {pkg => src/langbot/pkg}/pipeline/pool.py (100%) rename {pkg => src/langbot/pkg}/pipeline/preproc/__init__.py (100%) rename {pkg => src/langbot/pkg}/pipeline/preproc/preproc.py (100%) rename {pkg => src/langbot/pkg}/pipeline/process/__init__.py (100%) rename {pkg => src/langbot/pkg}/pipeline/process/handler.py (100%) rename {pkg => src/langbot/pkg}/pipeline/process/handlers/__init__.py (100%) rename {pkg => src/langbot/pkg}/pipeline/process/handlers/chat.py (100%) rename {pkg => src/langbot/pkg}/pipeline/process/handlers/command.py (100%) rename {pkg => src/langbot/pkg}/pipeline/process/process.py (100%) rename {pkg => src/langbot/pkg}/pipeline/ratelimit/__init__.py (100%) rename {pkg => src/langbot/pkg}/pipeline/ratelimit/algo.py (100%) rename {pkg => src/langbot/pkg}/pipeline/ratelimit/algos/__init__.py (100%) rename {pkg => src/langbot/pkg}/pipeline/ratelimit/algos/fixedwin.py (100%) rename {pkg => src/langbot/pkg}/pipeline/ratelimit/ratelimit.py (100%) rename {pkg => src/langbot/pkg}/pipeline/respback/__init__.py (100%) rename {pkg => src/langbot/pkg}/pipeline/respback/respback.py (100%) rename {pkg => src/langbot/pkg}/pipeline/resprule/__init__.py (100%) rename {pkg => src/langbot/pkg}/pipeline/resprule/entities.py (100%) rename {pkg => src/langbot/pkg}/pipeline/resprule/resprule.py (100%) rename {pkg => src/langbot/pkg}/pipeline/resprule/rule.py (100%) rename {pkg => src/langbot/pkg}/pipeline/resprule/rules/__init__.py (100%) rename {pkg => src/langbot/pkg}/pipeline/resprule/rules/atbot.py (91%) rename {pkg => src/langbot/pkg}/pipeline/resprule/rules/prefix.py (100%) rename {pkg => src/langbot/pkg}/pipeline/resprule/rules/random.py (100%) rename {pkg => src/langbot/pkg}/pipeline/resprule/rules/regexp.py (100%) rename {pkg => src/langbot/pkg}/pipeline/stage.py (100%) rename {pkg => src/langbot/pkg}/pipeline/wrapper/__init__.py (100%) rename {pkg => src/langbot/pkg}/pipeline/wrapper/wrapper.py (100%) rename {pkg => src/langbot/pkg}/platform/__init__.py (100%) rename {pkg => src/langbot/pkg}/platform/botmgr.py (96%) rename {pkg => src/langbot/pkg}/platform/logger.py (100%) rename {pkg => src/langbot/pkg}/platform/sources/__init__.py (100%) rename {pkg => src/langbot/pkg}/platform/sources/aiocqhttp.py (100%) rename {pkg => src/langbot/pkg}/platform/sources/aiocqhttp.yaml (100%) rename {pkg => src/langbot/pkg}/platform/sources/dingtalk.py (95%) rename {pkg => src/langbot/pkg}/platform/sources/dingtalk.svg (100%) rename {pkg => src/langbot/pkg}/platform/sources/dingtalk.yaml (100%) rename {pkg => src/langbot/pkg}/platform/sources/discord.py (99%) rename {pkg => src/langbot/pkg}/platform/sources/discord.svg (100%) rename {pkg => src/langbot/pkg}/platform/sources/discord.yaml (100%) rename {pkg => src/langbot/pkg}/platform/sources/lark.py (99%) rename {pkg => src/langbot/pkg}/platform/sources/lark.svg (100%) rename {pkg => src/langbot/pkg}/platform/sources/lark.yaml (100%) rename {pkg => src/langbot/pkg}/platform/sources/legacy/gewechat.png (100%) rename {pkg => src/langbot/pkg}/platform/sources/legacy/gewechat.py (100%) rename {pkg => src/langbot/pkg}/platform/sources/legacy/gewechat.yaml (100%) rename {pkg => src/langbot/pkg}/platform/sources/legacy/nakuru.png (100%) rename {pkg => src/langbot/pkg}/platform/sources/legacy/nakuru.py (100%) rename {pkg => src/langbot/pkg}/platform/sources/legacy/nakuru.yaml (100%) rename {pkg => src/langbot/pkg}/platform/sources/legacy/qqbotpy.py (100%) rename {pkg => src/langbot/pkg}/platform/sources/legacy/qqbotpy.svg (100%) rename {pkg => src/langbot/pkg}/platform/sources/legacy/qqbotpy.yaml (100%) rename {pkg => src/langbot/pkg}/platform/sources/line.png (100%) rename {pkg => src/langbot/pkg}/platform/sources/line.py (80%) rename {pkg => src/langbot/pkg}/platform/sources/line.yaml (100%) rename {pkg => src/langbot/pkg}/platform/sources/officialaccount.png (100%) rename {pkg => src/langbot/pkg}/platform/sources/officialaccount.py (93%) rename {pkg => src/langbot/pkg}/platform/sources/officialaccount.yaml (100%) rename {pkg => src/langbot/pkg}/platform/sources/onebot.png (100%) rename {pkg => src/langbot/pkg}/platform/sources/qqofficial.py (95%) rename {pkg => src/langbot/pkg}/platform/sources/qqofficial.svg (100%) rename {pkg => src/langbot/pkg}/platform/sources/qqofficial.yaml (100%) rename {pkg => src/langbot/pkg}/platform/sources/slack.png (100%) rename {pkg => src/langbot/pkg}/platform/sources/slack.py (97%) rename {pkg => src/langbot/pkg}/platform/sources/slack.yaml (100%) rename {pkg => src/langbot/pkg}/platform/sources/telegram.py (100%) rename {pkg => src/langbot/pkg}/platform/sources/telegram.svg (100%) rename {pkg => src/langbot/pkg}/platform/sources/telegram.yaml (100%) rename {pkg => src/langbot/pkg}/platform/sources/webchat.py (100%) rename {pkg => src/langbot/pkg}/platform/sources/webchat.yaml (100%) rename {pkg => src/langbot/pkg}/platform/sources/wechatpad.png (100%) rename {pkg => src/langbot/pkg}/platform/sources/wechatpad.py (99%) rename {pkg => src/langbot/pkg}/platform/sources/wechatpad.yaml (100%) rename {pkg => src/langbot/pkg}/platform/sources/wecom.png (100%) rename {pkg => src/langbot/pkg}/platform/sources/wecom.py (96%) rename {pkg => src/langbot/pkg}/platform/sources/wecom.yaml (100%) rename {pkg => src/langbot/pkg}/platform/sources/wecombot.png (100%) rename {pkg => src/langbot/pkg}/platform/sources/wecombot.py (90%) rename {pkg => src/langbot/pkg}/platform/sources/wecombot.yaml (100%) rename {pkg => src/langbot/pkg}/platform/sources/wecomcs.py (98%) rename {pkg => src/langbot/pkg}/platform/sources/wecomcs.yaml (100%) rename {pkg => src/langbot/pkg}/platform/webhook_pusher.py (100%) rename {pkg => src/langbot/pkg}/plugin/__init__.py (100%) rename {pkg => src/langbot/pkg}/plugin/connector.py (98%) rename {pkg => src/langbot/pkg}/plugin/handler.py (100%) rename {pkg => src/langbot/pkg}/provider/__init__.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/__init__.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/entities.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/errors.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/modelmgr.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requester.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requester.yaml (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/302ai.png (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/302aichatcmpl.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/302aichatcmpl.yaml (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/__init__.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/anthropic.svg (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/anthropicmsgs.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/anthropicmsgs.yaml (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/bailian.png (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/bailianchatcmpl.py (88%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/bailianchatcmpl.yaml (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/chatcmpl.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/chatcmpl.yaml (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/compshare.png (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/compsharechatcmpl.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/compsharechatcmpl.yaml (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/deepseek.svg (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/deepseekchatcmpl.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/deepseekchatcmpl.yaml (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/gemini.svg (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/geminichatcmpl.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/geminichatcmpl.yaml (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/giteeai.svg (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/giteeaichatcmpl.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/giteeaichatcmpl.yaml (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/jiekouai.png (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/jiekouaichatcmpl.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/jiekouaichatcmpl.yaml (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/lmstudio.webp (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/lmstudiochatcmpl.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/lmstudiochatcmpl.yaml (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/modelscope.svg (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/modelscopechatcmpl.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/modelscopechatcmpl.yaml (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/moonshot.png (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/moonshotchatcmpl.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/moonshotchatcmpl.yaml (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/newapi.png (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/newapichatcmpl.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/newapichatcmpl.yaml (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/ollama.svg (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/ollamachat.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/ollamachat.yaml (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/openai.svg (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/openrouter.svg (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/openrouterchatcmpl.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/openrouterchatcmpl.yaml (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/ppio.svg (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/ppiochatcmpl.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/ppiochatcmpl.yaml (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/qhaigc.png (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/qhaigcchatcmpl.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/qhaigcchatcmpl.yaml (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/shengsuanyun.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/shengsuanyun.svg (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/shengsuanyun.yaml (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/siliconflow.svg (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/siliconflowchatcmpl.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/siliconflowchatcmpl.yaml (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/tokenpony.svg (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/tokenpony.yaml (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/tokenponychatcmpl.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/volcark.svg (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/volcarkchatcmpl.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/volcarkchatcmpl.yaml (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/xai.svg (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/xaichatcmpl.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/xaichatcmpl.yaml (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/zhipuai.svg (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/zhipuaichatcmpl.py (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/requesters/zhipuaichatcmpl.yaml (100%) rename {pkg => src/langbot/pkg}/provider/modelmgr/token.py (100%) rename {pkg => src/langbot/pkg}/provider/runner.py (70%) rename {pkg => src/langbot/pkg}/provider/runners/__init__.py (100%) rename {pkg => src/langbot/pkg}/provider/runners/cozeapi.py (77%) rename {pkg => src/langbot/pkg}/provider/runners/dashscopeapi.py (100%) rename {pkg => src/langbot/pkg}/provider/runners/difysvapi.py (99%) rename {pkg => src/langbot/pkg}/provider/runners/langflowapi.py (100%) rename {pkg => src/langbot/pkg}/provider/runners/localagent.py (99%) rename {pkg => src/langbot/pkg}/provider/runners/n8nsvapi.py (100%) rename {pkg => src/langbot/pkg}/provider/runners/tboxapi.py (93%) rename {pkg => src/langbot/pkg}/provider/session/__init__.py (100%) rename {pkg => src/langbot/pkg}/provider/session/sessionmgr.py (100%) rename {pkg => src/langbot/pkg}/provider/tools/__init__.py (100%) rename {pkg => src/langbot/pkg}/provider/tools/loader.py (100%) rename {pkg => src/langbot/pkg}/provider/tools/loaders/__init__.py (100%) rename {pkg => src/langbot/pkg}/provider/tools/loaders/mcp.py (100%) rename {pkg => src/langbot/pkg}/provider/tools/loaders/plugin.py (100%) rename {pkg => src/langbot/pkg}/provider/tools/toolmgr.py (90%) rename {pkg => src/langbot/pkg}/rag/knowledge/kbmgr.py (96%) rename {pkg => src/langbot/pkg}/rag/knowledge/services/__init__.py (100%) rename {pkg => src/langbot/pkg}/rag/knowledge/services/base_service.py (100%) rename {pkg => src/langbot/pkg}/rag/knowledge/services/chunker.py (94%) rename {pkg => src/langbot/pkg}/rag/knowledge/services/embedder.py (86%) rename {pkg => src/langbot/pkg}/rag/knowledge/services/parser.py (99%) rename {pkg => src/langbot/pkg}/rag/knowledge/services/retriever.py (100%) rename {pkg => src/langbot/pkg}/storage/__init__.py (100%) rename {pkg => src/langbot/pkg}/storage/mgr.py (100%) rename {pkg => src/langbot/pkg}/storage/provider.py (100%) rename {pkg => src/langbot/pkg}/storage/providers/__init__.py (100%) rename {pkg => src/langbot/pkg}/storage/providers/localstorage.py (100%) rename {pkg => src/langbot/pkg}/storage/providers/s3storage.py (100%) rename {pkg => src/langbot/pkg}/utils/__init__.py (100%) rename {pkg => src/langbot/pkg}/utils/constants.py (83%) rename {pkg => src/langbot/pkg}/utils/funcschema.py (100%) rename {pkg => src/langbot/pkg}/utils/image.py (100%) rename {pkg => src/langbot/pkg}/utils/importutil.py (58%) rename {pkg => src/langbot/pkg}/utils/logcache.py (100%) create mode 100644 src/langbot/pkg/utils/paths.py rename {pkg => src/langbot/pkg}/utils/pkgmgr.py (100%) rename {pkg => src/langbot/pkg}/utils/platform.py (100%) rename {pkg => src/langbot/pkg}/utils/proxy.py (100%) rename {pkg => src/langbot/pkg}/utils/version.py (98%) rename {pkg => src/langbot/pkg}/vector/__init__.py (100%) rename {pkg => src/langbot/pkg}/vector/mgr.py (100%) rename {pkg => src/langbot/pkg}/vector/vdb.py (100%) rename {pkg => src/langbot/pkg}/vector/vdbs/__init__.py (100%) rename {pkg => src/langbot/pkg}/vector/vdbs/chroma.py (96%) rename {pkg => src/langbot/pkg}/vector/vdbs/qdrant.py (98%) rename {templates => src/langbot/templates}/__init__.py (100%) rename components.yaml => src/langbot/templates/components.yaml (78%) rename {templates => src/langbot/templates}/config.yaml (100%) rename {templates => src/langbot/templates}/default-pipeline-config.json (100%) rename {templates => src/langbot/templates}/legacy/command.json (100%) rename {templates => src/langbot/templates}/legacy/pipeline.json (100%) rename {templates => src/langbot/templates}/legacy/platform.json (100%) rename {templates => src/langbot/templates}/legacy/provider.json (100%) rename {templates => src/langbot/templates}/legacy/system.json (100%) rename {templates => src/langbot/templates}/metadata/pipeline/ai.yaml (100%) rename {templates => src/langbot/templates}/metadata/pipeline/output.yaml (100%) rename {templates => src/langbot/templates}/metadata/pipeline/safety.yaml (100%) rename {templates => src/langbot/templates}/metadata/pipeline/trigger.yaml (100%) rename {templates => src/langbot/templates}/metadata/sensitive-words.json (100%) diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml new file mode 100644 index 00000000..a03f51ef --- /dev/null +++ b/.github/workflows/publish-to-pypi.yml @@ -0,0 +1,46 @@ +name: Build and Publish to PyPI + +on: + workflow_dispatch: + release: + types: [published] + +jobs: + build-and-publish: + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write # Required for trusted publishing to PyPI + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + + - name: Build frontend + run: | + cd web + npm install -g pnpm + pnpm install + pnpm build + mkdir -p ../src/langbot/web/out + cp -r out ../src/langbot/web/ + + - name: Install the latest version of uv + uses: astral-sh/setup-uv@v6 + with: + version: "latest" + + - name: Build package + run: | + uv build + + - name: Publish to PyPI + run: | + uv publish --token ${{ secrets.PYPI_TOKEN }} diff --git a/.gitignore b/.gitignore index ecfb7207..2a0b6245 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,9 @@ uv.lock plugins.bak coverage.xml .coverage +src/langbot/web/ + +# Build artifacts +/dist +/build +*.egg-info diff --git a/README.md b/README.md index 754f6258..94cc3870 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,25 @@ LangBot 是一个开源的大语言模型原生即时通信机器人开发平台 ## 📦 开始使用 +#### 快速体验(推荐) + +使用 `uvx` 一键启动(无需安装): + +```bash +uvx langbot +``` + +或使用 `pip` 安装后运行: + +```bash +pip install langbot +langbot +``` + +访问 http://localhost:5300 即可开始使用。 + +详细文档[PyPI 安装](docs/PYPI_INSTALLATION.md)。 + #### Docker Compose 部署 ```bash diff --git a/README_EN.md b/README_EN.md index e020beba..79117d44 100644 --- a/README_EN.md +++ b/README_EN.md @@ -25,6 +25,25 @@ LangBot is an open-source LLM native instant messaging robot development platfor ## 📦 Getting Started +#### Quick Start (Recommended) + +Use `uvx` to start with one command (no installation required): + +```bash +uvx langbot +``` + +Or install with `pip` and run: + +```bash +pip install langbot +langbot +``` + +Visit http://localhost:5300 to start using it. + +Detailed documentation [PyPI Installation](docs/PYPI_INSTALLATION.md). + #### Docker Compose Deployment ```bash diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index f9bb6ffa..6d2ec1fc 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -7,6 +7,7 @@ services: langbot_plugin_runtime: image: rockchin/langbot:latest container_name: langbot_plugin_runtime + platform: linux/amd64 # For Apple Silicon compatibility volumes: - ./data/plugins:/app/data/plugins ports: @@ -21,6 +22,7 @@ services: langbot: image: rockchin/langbot:latest container_name: langbot + platform: linux/amd64 # For Apple Silicon compatibility volumes: - ./data:/app/data - ./plugins:/app/plugins diff --git a/docs/PYPI_INSTALLATION.md b/docs/PYPI_INSTALLATION.md new file mode 100644 index 00000000..1144d5cb --- /dev/null +++ b/docs/PYPI_INSTALLATION.md @@ -0,0 +1,117 @@ +# LangBot PyPI Package Installation + +## Quick Start with uvx + +The easiest way to run LangBot is using `uvx` (recommended for quick testing): + +```bash +uvx langbot +``` + +This will automatically download and run the latest version of LangBot. + +## Install with pip/uv + +You can also install LangBot as a regular Python package: + +```bash +# Using pip +pip install langbot + +# Using uv +uv pip install langbot +``` + +Then run it: + +```bash +langbot +``` + +Or using Python module syntax: + +```bash +python -m langbot +``` + +## Installation with Frontend + +When published to PyPI, the LangBot package includes the pre-built frontend files. You don't need to build the frontend separately. + +## Data Directory + +When running LangBot as a package, it will create a `data/` directory in your current working directory to store configuration, logs, and other runtime data. You can run LangBot from any directory, and it will set up its data directory there. + +## Command Line Options + +LangBot supports the following command line options: + +- `--standalone-runtime`: Use standalone plugin runtime +- `--debug`: Enable debug mode + +Example: + +```bash +langbot --debug +``` + +## Comparison with Other Installation Methods + +### PyPI Package (uvx/pip) +- **Pros**: Easy to install and update, no need to clone repository or build frontend +- **Cons**: Less flexible for development/customization + +### Docker +- **Pros**: Isolated environment, easy deployment +- **Cons**: Requires Docker + +### Manual Source Installation +- **Pros**: Full control, easy to customize and develop +- **Cons**: Requires building frontend, managing dependencies manually + +## Development + +If you want to contribute or customize LangBot, you should still use the manual installation method by cloning the repository: + +```bash +git clone https://github.com/langbot-app/LangBot +cd LangBot +uv sync +cd web +npm install +npm run build +cd .. +uv run main.py +``` + +## Updating + +To update to the latest version: + +```bash +# With pip +pip install --upgrade langbot + +# With uv +uv pip install --upgrade langbot + +# With uvx (automatically uses latest) +uvx langbot +``` + +## System Requirements + +- Python 3.10.1 or higher +- Operating System: Linux, macOS, or Windows + +## Differences from Source Installation + +When running LangBot from the PyPI package (via uvx or pip), there are a few behavioral differences compared to running from source: + +1. **Version Check**: The package version does not prompt for user input when the Python version is incompatible. It simply prints an error message and exits. This makes it compatible with non-interactive environments like containers and CI/CD. + +2. **Working Directory**: The package version does not require being run from the LangBot project root. You can run `langbot` from any directory, and it will create a `data/` directory in your current working directory. + +3. **Frontend Files**: The frontend is pre-built and included in the package, so you don't need to run `npm build` separately. + +These differences are intentional to make the package more user-friendly and suitable for various deployment scenarios. diff --git a/libs/dify_service_api/test.py b/libs/dify_service_api/test.py deleted file mode 100644 index 7da04f6b..00000000 --- a/libs/dify_service_api/test.py +++ /dev/null @@ -1,45 +0,0 @@ -from v1 import client # type: ignore - -import asyncio - -import os -import json - - -class TestDifyClient: - async def test_chat_messages(self): - cln = client.AsyncDifyServiceClient(api_key=os.getenv('DIFY_API_KEY'), base_url=os.getenv('DIFY_BASE_URL')) - - async for chunk in cln.chat_messages(inputs={}, query='调用工具查看现在几点?', user='test'): - print(json.dumps(chunk, ensure_ascii=False, indent=4)) - - async def test_upload_file(self): - cln = client.AsyncDifyServiceClient(api_key=os.getenv('DIFY_API_KEY'), base_url=os.getenv('DIFY_BASE_URL')) - - file_bytes = open('img.png', 'rb').read() - - print(type(file_bytes)) - - file = ('img2.png', file_bytes, 'image/png') - - resp = await cln.upload_file(file=file, user='test') - print(json.dumps(resp, ensure_ascii=False, indent=4)) - - async def test_workflow_run(self): - cln = client.AsyncDifyServiceClient(api_key=os.getenv('DIFY_API_KEY'), base_url=os.getenv('DIFY_BASE_URL')) - - # resp = await cln.workflow_run(inputs={}, user="test") - # # print(json.dumps(resp, ensure_ascii=False, indent=4)) - # print(resp) - chunks = [] - - ignored_events = ['text_chunk'] - async for chunk in cln.workflow_run(inputs={}, user='test'): - if chunk['event'] in ignored_events: - continue - chunks.append(chunk) - print(json.dumps(chunks, ensure_ascii=False, indent=4)) - - -if __name__ == '__main__': - asyncio.run(TestDifyClient().test_chat_messages()) diff --git a/main.py b/main.py index 708a01cd..9e1f5c31 100644 --- a/main.py +++ b/main.py @@ -1,117 +1,3 @@ -import asyncio -import argparse -# LangBot 终端启动入口 -# 在此层级解决依赖项检查。 -# LangBot/main.py +import langbot.__main__ -asciiart = r""" - _ ___ _ -| | __ _ _ _ __ _| _ ) ___| |_ -| |__/ _` | ' \/ _` | _ \/ _ \ _| -|____\__,_|_||_\__, |___/\___/\__| - |___/ - -⭐️ Open Source 开源地址: https://github.com/langbot-app/LangBot -📖 Documentation 文档地址: https://docs.langbot.app -""" - - -async def main_entry(loop: asyncio.AbstractEventLoop): - parser = argparse.ArgumentParser(description='LangBot') - parser.add_argument( - '--standalone-runtime', - action='store_true', - help='Use standalone plugin runtime / 使用独立插件运行时', - default=False, - ) - parser.add_argument('--debug', action='store_true', help='Debug mode / 调试模式', default=False) - args = parser.parse_args() - - if args.standalone_runtime: - from pkg.utils import platform - - platform.standalone_runtime = True - - if args.debug: - from pkg.utils import constants - - constants.debug_mode = True - - print(asciiart) - - import sys - - # 检查依赖 - - from pkg.core.bootutils import deps - - missing_deps = await deps.check_deps() - - if missing_deps: - print('以下依赖包未安装,将自动安装,请完成后重启程序:') - print( - 'These dependencies are missing, they will be installed automatically, please restart the program after completion:' - ) - for dep in missing_deps: - print('-', dep) - await deps.install_deps(missing_deps) - print('已自动安装缺失的依赖包,请重启程序。') - print('The missing dependencies have been installed automatically, please restart the program.') - sys.exit(0) - - # # 检查pydantic版本,如果没有 pydantic.v1,则把 pydantic 映射为 v1 - # import pydantic.version - - # if pydantic.version.VERSION < '2.0': - # import pydantic - - # sys.modules['pydantic.v1'] = pydantic - - # 检查配置文件 - - from pkg.core.bootutils import files - - generated_files = await files.generate_files() - - if generated_files: - print('以下文件不存在,已自动生成:') - print('Following files do not exist and have been automatically generated:') - for file in generated_files: - print('-', file) - - from pkg.core import boot - - await boot.main(loop) - - -if __name__ == '__main__': - import os - import sys - - # 必须大于 3.10.1 - if sys.version_info < (3, 10, 1): - print('需要 Python 3.10.1 及以上版本,当前 Python 版本为:', sys.version) - input('按任意键退出...') - print('Your Python version is not supported. Please exit the program by pressing any key.') - exit(1) - - # Check if the current directory is the LangBot project root directory - invalid_pwd = False - - if not os.path.exists('main.py'): - invalid_pwd = True - else: - with open('main.py', 'r', encoding='utf-8') as f: - content = f.read() - if 'LangBot/main.py' not in content: - invalid_pwd = True - if invalid_pwd: - print('请在 LangBot 项目根目录下以命令形式运行此程序。') - input('按任意键退出...') - print('Please run this program in the LangBot project root directory in command form.') - print('Press any key to exit...') - exit(1) - - loop = asyncio.new_event_loop() - - loop.run_until_complete(main_entry(loop)) +langbot.__main__.main() diff --git a/pkg/utils/announce.py b/pkg/utils/announce.py deleted file mode 100644 index 8778a04f..00000000 --- a/pkg/utils/announce.py +++ /dev/null @@ -1,115 +0,0 @@ -from __future__ import annotations - -import json -import typing -import os -import base64 -import logging - -import pydantic -import requests - -from ..core import app - - -class Announcement(pydantic.BaseModel): - """公告""" - - id: int - - time: str - - timestamp: int - - content: str - - enabled: typing.Optional[bool] = True - - def to_dict(self) -> dict: - return { - 'id': self.id, - 'time': self.time, - 'timestamp': self.timestamp, - 'content': self.content, - 'enabled': self.enabled, - } - - -class AnnouncementManager: - """公告管理器""" - - ap: app.Application = None - - def __init__(self, ap: app.Application): - self.ap = ap - - async def fetch_all(self) -> list[Announcement]: - """获取所有公告""" - try: - resp = requests.get( - url='https://api.github.com/repos/langbot-app/LangBot/contents/res/announcement.json', - proxies=self.ap.proxy_mgr.get_forward_proxies(), - timeout=5, - ) - resp.raise_for_status() # 检查请求是否成功 - obj_json = resp.json() - b64_content = obj_json['content'] - # 解码 - content = base64.b64decode(b64_content).decode('utf-8') - - return [Announcement(**item) for item in json.loads(content)] - except (requests.RequestException, json.JSONDecodeError, KeyError) as e: - self.ap.logger.warning(f'获取公告失败: {e}') - pass - return [] # 请求失败时返回空列表 - - async def fetch_saved(self) -> list[Announcement]: - if not os.path.exists('data/labels/announcement_saved.json'): - with open('data/labels/announcement_saved.json', 'w', encoding='utf-8') as f: - f.write('[]') - - with open('data/labels/announcement_saved.json', 'r', encoding='utf-8') as f: - content = f.read() - - if not content: - content = '[]' - - return [Announcement(**item) for item in json.loads(content)] - - async def write_saved(self, content: list[Announcement]): - with open('data/labels/announcement_saved.json', 'w', encoding='utf-8') as f: - f.write(json.dumps([item.to_dict() for item in content], indent=4, ensure_ascii=False)) - - async def fetch_new(self) -> list[Announcement]: - """获取新公告""" - all = await self.fetch_all() - saved = await self.fetch_saved() - - to_show: list[Announcement] = [] - - for item in all: - # 遍历saved检查是否有相同id的公告 - for saved_item in saved: - if saved_item.id == item.id: - break - else: - if item.enabled: - # 没有相同id的公告 - to_show.append(item) - - await self.write_saved(all) - return to_show - - async def show_announcements(self) -> typing.Tuple[str, int]: - """显示公告""" - try: - announcements = await self.fetch_new() - ann_text = '' - for ann in announcements: - ann_text += f'[公告] {ann.time}: {ann.content}\n' - - # TODO statistics - - return ann_text, logging.INFO - except Exception as e: - return f'获取公告时出错: {e}', logging.WARNING diff --git a/pyproject.toml b/pyproject.toml index 0dbe4fef..a5549e5c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,8 +1,9 @@ [project] name = "langbot" -version = "4.5.0" +version = "4.6.0-beta.2" description = "Easy-to-use global IM bot platform designed for LLM era" readme = "README.md" +license-files = ["LICENSE"] requires-python = ">=3.10.1,<4.0" dependencies = [ "aiocqhttp>=1.4.4", @@ -45,7 +46,6 @@ dependencies = [ "urllib3>=2.4.0", "websockets>=15.0.1", "python-socks>=2.7.1", # dingtalk missing dependency - "taskgroup==0.0.0a4", # graingert/taskgroup#20 "pip>=25.1.1", "ruff>=0.11.9", "pre-commit>=4.2.0", @@ -85,11 +85,10 @@ keywords = [ "onebot", ] classifiers = [ - "Development Status :: 3 - Alpha", + "Development Status :: 5 - Production/Stable", "Framework :: AsyncIO", "Framework :: Robot Framework", "Framework :: Robot Framework :: Library", - "License :: OSI Approved :: AGPL-3 License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Topic :: Communications :: Chat", @@ -100,6 +99,16 @@ Homepage = "https://langbot.app" Documentation = "https://docs.langbot.app" Repository = "https://github.com/langbot-app/LangBot" +[project.scripts] +langbot = "langbot.__main__:main" + +[build-system] +requires = ["setuptools>=61.0", "wheel"] +build-backend = "setuptools.build_meta" + +[tool.setuptools] +package-data = { "langbot" = ["templates/**", "pkg/provider/modelmgr/requesters/*", "pkg/platform/sources/*", "web/out/**"] } + [dependency-groups] dev = [ "pre-commit>=4.2.0", diff --git a/pytest.ini b/pytest.ini index 80cda02e..ef5b705d 100644 --- a/pytest.ini +++ b/pytest.ini @@ -26,7 +26,7 @@ markers = # Coverage options (when using pytest-cov) [coverage:run] -source = pkg +source = langbot.pkg omit = */tests/* */test_*.py diff --git a/src/langbot/__init__.py b/src/langbot/__init__.py new file mode 100644 index 00000000..fdaf8272 --- /dev/null +++ b/src/langbot/__init__.py @@ -0,0 +1,3 @@ +"""LangBot - Easy-to-use global IM bot platform designed for LLM era""" + +__version__ = '4.6.0-beta.2' diff --git a/src/langbot/__main__.py b/src/langbot/__main__.py new file mode 100644 index 00000000..b94500e7 --- /dev/null +++ b/src/langbot/__main__.py @@ -0,0 +1,104 @@ +"""LangBot entry point for package execution""" + +import asyncio +import argparse +import sys +import os + +# ASCII art banner +asciiart = r""" + _ ___ _ +| | __ _ _ _ __ _| _ ) ___| |_ +| |__/ _` | ' \/ _` | _ \/ _ \ _| +|____\__,_|_||_\__, |___/\___/\__| + |___/ + +⭐️ Open Source 开源地址: https://github.com/langbot-app/LangBot +📖 Documentation 文档地址: https://docs.langbot.app +""" + + +async def main_entry(loop: asyncio.AbstractEventLoop): + """Main entry point for LangBot""" + parser = argparse.ArgumentParser(description='LangBot') + parser.add_argument( + '--standalone-runtime', + action='store_true', + help='Use standalone plugin runtime / 使用独立插件运行时', + default=False, + ) + parser.add_argument('--debug', action='store_true', help='Debug mode / 调试模式', default=False) + args = parser.parse_args() + + if args.standalone_runtime: + from langbot.pkg.utils import platform + + platform.standalone_runtime = True + + if args.debug: + from langbot.pkg.utils import constants + + constants.debug_mode = True + + print(asciiart) + + # Check dependencies + from langbot.pkg.core.bootutils import deps + + missing_deps = await deps.check_deps() + + if missing_deps: + print('以下依赖包未安装,将自动安装,请完成后重启程序:') + print( + 'These dependencies are missing, they will be installed automatically, please restart the program after completion:' + ) + for dep in missing_deps: + print('-', dep) + await deps.install_deps(missing_deps) + print('已自动安装缺失的依赖包,请重启程序。') + print('The missing dependencies have been installed automatically, please restart the program.') + sys.exit(0) + + # Check configuration files + from langbot.pkg.core.bootutils import files + + generated_files = await files.generate_files() + + if generated_files: + print('以下文件不存在,已自动生成:') + print('Following files do not exist and have been automatically generated:') + for file in generated_files: + print('-', file) + + from langbot.pkg.core import boot + + await boot.main(loop) + + +def main(): + """Main function to be called by console script entry point""" + # Check Python version + if sys.version_info < (3, 10, 1): + print('需要 Python 3.10.1 及以上版本,当前 Python 版本为:', sys.version) + print('Your Python version is not supported.') + print('Python 3.10.1 or higher is required. Current version:', sys.version) + sys.exit(1) + + # Set up the working directory + # When installed as a package, we need to handle the working directory differently + # We'll create data directory in current working directory if not exists + os.makedirs('data', exist_ok=True) + + loop = asyncio.new_event_loop() + + try: + loop.run_until_complete(main_entry(loop)) + except KeyboardInterrupt: + print('\n正在退出...') + print('Exiting...') + finally: + loop.close() + + +if __name__ == '__main__': + main() diff --git a/libs/LICENSE b/src/langbot/libs/LICENSE similarity index 100% rename from libs/LICENSE rename to src/langbot/libs/LICENSE diff --git a/libs/README.md b/src/langbot/libs/README.md similarity index 100% rename from libs/README.md rename to src/langbot/libs/README.md diff --git a/libs/coze_server_api/__init__.py b/src/langbot/libs/coze_server_api/__init__.py similarity index 100% rename from libs/coze_server_api/__init__.py rename to src/langbot/libs/coze_server_api/__init__.py diff --git a/libs/coze_server_api/client.py b/src/langbot/libs/coze_server_api/client.py similarity index 60% rename from libs/coze_server_api/client.py rename to src/langbot/libs/coze_server_api/client.py index 8d35a102..54fb4749 100644 --- a/libs/coze_server_api/client.py +++ b/src/langbot/libs/coze_server_api/client.py @@ -7,10 +7,8 @@ import os from pathlib import Path - - class AsyncCozeAPIClient: - def __init__(self, api_key: str, api_base: str = "https://api.coze.cn"): + def __init__(self, api_key: str, api_base: str = 'https://api.coze.cn'): self.api_key = api_key self.api_base = api_base self.session = None @@ -24,13 +22,11 @@ class AsyncCozeAPIClient: """退出时自动关闭会话""" await self.close() - - async def coze_session(self): """确保HTTP session存在""" if self.session is None: connector = aiohttp.TCPConnector( - ssl=False if self.api_base.startswith("http://") else True, + ssl=False if self.api_base.startswith('http://') else True, limit=100, limit_per_host=30, keepalive_timeout=30, @@ -42,12 +38,10 @@ class AsyncCozeAPIClient: sock_read=120, ) headers = { - "Authorization": f"Bearer {self.api_key}", - "Accept": "text/event-stream", + 'Authorization': f'Bearer {self.api_key}', + 'Accept': 'text/event-stream', } - self.session = aiohttp.ClientSession( - headers=headers, timeout=timeout, connector=connector - ) + self.session = aiohttp.ClientSession(headers=headers, timeout=timeout, connector=connector) return self.session async def close(self): @@ -63,15 +57,15 @@ class AsyncCozeAPIClient: # 处理 Path 对象 if isinstance(file, Path): if not file.exists(): - raise ValueError(f"File not found: {file}") - with open(file, "rb") as f: + raise ValueError(f'File not found: {file}') + with open(file, 'rb') as f: file = f.read() # 处理文件路径字符串 elif isinstance(file, str): if not os.path.isfile(file): - raise ValueError(f"File not found: {file}") - with open(file, "rb") as f: + raise ValueError(f'File not found: {file}') + with open(file, 'rb') as f: file = f.read() # 处理文件对象 @@ -79,43 +73,39 @@ class AsyncCozeAPIClient: file = file.read() session = await self.coze_session() - url = f"{self.api_base}/v1/files/upload" + url = f'{self.api_base}/v1/files/upload' try: file_io = io.BytesIO(file) async with session.post( url, data={ - "file": file_io, + 'file': file_io, }, timeout=aiohttp.ClientTimeout(total=60), ) as response: if response.status == 401: - raise Exception("Coze API 认证失败,请检查 API Key 是否正确") + raise Exception('Coze API 认证失败,请检查 API Key 是否正确') response_text = await response.text() - if response.status != 200: - raise Exception( - f"文件上传失败,状态码: {response.status}, 响应: {response_text}" - ) + raise Exception(f'文件上传失败,状态码: {response.status}, 响应: {response_text}') try: result = await response.json() except json.JSONDecodeError: - raise Exception(f"文件上传响应解析失败: {response_text}") + raise Exception(f'文件上传响应解析失败: {response_text}') - if result.get("code") != 0: - raise Exception(f"文件上传失败: {result.get('msg', '未知错误')}") + if result.get('code') != 0: + raise Exception(f'文件上传失败: {result.get("msg", "未知错误")}') - file_id = result["data"]["id"] + file_id = result['data']['id'] return file_id except asyncio.TimeoutError: - raise Exception("文件上传超时") + raise Exception('文件上传超时') except Exception as e: - raise Exception(f"文件上传失败: {str(e)}") - + raise Exception(f'文件上传失败: {str(e)}') async def chat_messages( self, @@ -139,22 +129,21 @@ class AsyncCozeAPIClient: timeout: 超时时间 """ session = await self.coze_session() - url = f"{self.api_base}/v3/chat" + url = f'{self.api_base}/v3/chat' payload = { - "bot_id": bot_id, - "user_id": user_id, - "stream": stream, - "auto_save_history": auto_save_history, + 'bot_id': bot_id, + 'user_id': user_id, + 'stream': stream, + 'auto_save_history': auto_save_history, } if additional_messages: - payload["additional_messages"] = additional_messages + payload['additional_messages'] = additional_messages params = {} if conversation_id: - params["conversation_id"] = conversation_id - + params['conversation_id'] = conversation_id try: async with session.post( @@ -164,29 +153,25 @@ class AsyncCozeAPIClient: timeout=aiohttp.ClientTimeout(total=timeout), ) as response: if response.status == 401: - raise Exception("Coze API 认证失败,请检查 API Key 是否正确") + raise Exception('Coze API 认证失败,请检查 API Key 是否正确') if response.status != 200: - raise Exception(f"Coze API 流式请求失败,状态码: {response.status}") - + raise Exception(f'Coze API 流式请求失败,状态码: {response.status}') async for chunk in response.content: - chunk = chunk.decode("utf-8") + chunk = chunk.decode('utf-8') if chunk != '\n': - if chunk.startswith("event:"): - chunk_type = chunk.replace("event:", "", 1).strip() - elif chunk.startswith("data:"): - chunk_data = chunk.replace("data:", "", 1).strip() + if chunk.startswith('event:'): + chunk_type = chunk.replace('event:', '', 1).strip() + elif chunk.startswith('data:'): + chunk_data = chunk.replace('data:', '', 1).strip() else: - yield {"event": chunk_type, "data": json.loads(chunk_data) if chunk_data else {}} # 处理本地部署时,接口返回的data为空值 + yield { + 'event': chunk_type, + 'data': json.loads(chunk_data) if chunk_data else {}, + } # 处理本地部署时,接口返回的data为空值 except asyncio.TimeoutError: - raise Exception(f"Coze API 流式请求超时 ({timeout}秒)") + raise Exception(f'Coze API 流式请求超时 ({timeout}秒)') except Exception as e: - raise Exception(f"Coze API 流式请求失败: {str(e)}") - - - - - - + raise Exception(f'Coze API 流式请求失败: {str(e)}') diff --git a/libs/dify_service_api/README.md b/src/langbot/libs/dify_service_api/README.md similarity index 100% rename from libs/dify_service_api/README.md rename to src/langbot/libs/dify_service_api/README.md diff --git a/libs/dify_service_api/__init__.py b/src/langbot/libs/dify_service_api/__init__.py similarity index 100% rename from libs/dify_service_api/__init__.py rename to src/langbot/libs/dify_service_api/__init__.py diff --git a/libs/dify_service_api/v1/__init__.py b/src/langbot/libs/dify_service_api/v1/__init__.py similarity index 100% rename from libs/dify_service_api/v1/__init__.py rename to src/langbot/libs/dify_service_api/v1/__init__.py diff --git a/libs/dify_service_api/v1/client.py b/src/langbot/libs/dify_service_api/v1/client.py similarity index 100% rename from libs/dify_service_api/v1/client.py rename to src/langbot/libs/dify_service_api/v1/client.py diff --git a/libs/dify_service_api/v1/client_test.py b/src/langbot/libs/dify_service_api/v1/client_test.py similarity index 100% rename from libs/dify_service_api/v1/client_test.py rename to src/langbot/libs/dify_service_api/v1/client_test.py diff --git a/libs/dify_service_api/v1/errors.py b/src/langbot/libs/dify_service_api/v1/errors.py similarity index 100% rename from libs/dify_service_api/v1/errors.py rename to src/langbot/libs/dify_service_api/v1/errors.py diff --git a/libs/dingtalk_api/EchoHandler.py b/src/langbot/libs/dingtalk_api/EchoHandler.py similarity index 100% rename from libs/dingtalk_api/EchoHandler.py rename to src/langbot/libs/dingtalk_api/EchoHandler.py diff --git a/libs/dingtalk_api/__init__.py b/src/langbot/libs/dingtalk_api/__init__.py similarity index 100% rename from libs/dingtalk_api/__init__.py rename to src/langbot/libs/dingtalk_api/__init__.py diff --git a/libs/dingtalk_api/api.py b/src/langbot/libs/dingtalk_api/api.py similarity index 96% rename from libs/dingtalk_api/api.py rename to src/langbot/libs/dingtalk_api/api.py index 03caabfd..abd68a40 100644 --- a/libs/dingtalk_api/api.py +++ b/src/langbot/libs/dingtalk_api/api.py @@ -194,28 +194,23 @@ class DingTalkClient: 'Type': 'richText', 'Elements': [], # 按顺序存储所有元素 'SimpleContent': '', # 兼容字段:纯文本内容 - 'SimplePicture': '' # 兼容字段:第一张图片 + 'SimplePicture': '', # 兼容字段:第一张图片 } # 先收集所有文本和图片占位符 text_elements = [] - image_placeholders = [] # 解析富文本内容,保持原始顺序 for item in data['richText']: - # 处理文本内容 - if 'text' in item and item['text'] != "\n": - element = { - 'Type': 'text', - 'Content': item['text'] - } + if 'text' in item and item['text'] != '\n': + element = {'Type': 'text', 'Content': item['text']} rich_content['Elements'].append(element) text_elements.append(item['text']) # 检查是否是图片元素 - 根据钉钉API的实际结构调整 # 钉钉富文本中的图片通常有特定标识,可能需要根据实际返回调整 - elif item.get("type") == "picture": + elif item.get('type') == 'picture': # 创建图片占位符 element = { 'Type': 'image_placeholder', @@ -232,10 +227,7 @@ class DingTalkClient: if element['Type'] == 'image_placeholder': if image_index < len(image_list) and image_list[image_index]: image_url = await self.download_image(image_list[image_index]) - new_elements.append({ - 'Type': 'image', - 'Picture': image_url - }) + new_elements.append({'Type': 'image', 'Picture': image_url}) image_index += 1 else: # 如果没有对应的图片,保留占位符或跳过 @@ -245,7 +237,6 @@ class DingTalkClient: rich_content['Elements'] = new_elements - # 设置兼容字段 all_texts = [elem['Content'] for elem in rich_content['Elements'] if elem.get('Type') == 'text'] rich_content['SimpleContent'] = '\n'.join(all_texts) if all_texts else '' @@ -261,8 +252,6 @@ class DingTalkClient: if all_images: message_data['Picture'] = all_images[0] - - elif incoming_message.message_type == 'text': message_data['Content'] = incoming_message.get_text_list()[0] diff --git a/libs/dingtalk_api/dingtalkevent.py b/src/langbot/libs/dingtalk_api/dingtalkevent.py similarity index 99% rename from libs/dingtalk_api/dingtalkevent.py rename to src/langbot/libs/dingtalk_api/dingtalkevent.py index f45264c6..29322bcb 100644 --- a/libs/dingtalk_api/dingtalkevent.py +++ b/src/langbot/libs/dingtalk_api/dingtalkevent.py @@ -43,7 +43,6 @@ class DingTalkEvent(dict): def name(self): return self.get('Name', '') - @property def conversation(self): return self.get('conversation_type', '') diff --git a/libs/official_account_api/__init__.py b/src/langbot/libs/official_account_api/__init__.py similarity index 100% rename from libs/official_account_api/__init__.py rename to src/langbot/libs/official_account_api/__init__.py diff --git a/libs/official_account_api/api.py b/src/langbot/libs/official_account_api/api.py similarity index 99% rename from libs/official_account_api/api.py rename to src/langbot/libs/official_account_api/api.py index 569196f8..57d41c49 100644 --- a/libs/official_account_api/api.py +++ b/src/langbot/libs/official_account_api/api.py @@ -1,12 +1,12 @@ # 微信公众号的加解密算法与企业微信一样,所以直接使用企业微信的加解密算法文件 import time import traceback -from libs.wecom_api.WXBizMsgCrypt3 import WXBizMsgCrypt +from langbot.libs.wecom_api.WXBizMsgCrypt3 import WXBizMsgCrypt import xml.etree.ElementTree as ET from quart import Quart, request import hashlib from typing import Callable -from .oaevent import OAEvent +from langbot.libs.official_account_api.oaevent import OAEvent import asyncio diff --git a/libs/official_account_api/oaevent.py b/src/langbot/libs/official_account_api/oaevent.py similarity index 100% rename from libs/official_account_api/oaevent.py rename to src/langbot/libs/official_account_api/oaevent.py diff --git a/libs/qq_official_api/__init__.py b/src/langbot/libs/qq_official_api/__init__.py similarity index 100% rename from libs/qq_official_api/__init__.py rename to src/langbot/libs/qq_official_api/__init__.py diff --git a/libs/qq_official_api/api.py b/src/langbot/libs/qq_official_api/api.py similarity index 100% rename from libs/qq_official_api/api.py rename to src/langbot/libs/qq_official_api/api.py diff --git a/libs/qq_official_api/qqofficialevent.py b/src/langbot/libs/qq_official_api/qqofficialevent.py similarity index 100% rename from libs/qq_official_api/qqofficialevent.py rename to src/langbot/libs/qq_official_api/qqofficialevent.py diff --git a/libs/slack_api/__init__.py b/src/langbot/libs/slack_api/__init__.py similarity index 100% rename from libs/slack_api/__init__.py rename to src/langbot/libs/slack_api/__init__.py diff --git a/libs/slack_api/api.py b/src/langbot/libs/slack_api/api.py similarity index 100% rename from libs/slack_api/api.py rename to src/langbot/libs/slack_api/api.py diff --git a/libs/slack_api/slackevent.py b/src/langbot/libs/slack_api/slackevent.py similarity index 100% rename from libs/slack_api/slackevent.py rename to src/langbot/libs/slack_api/slackevent.py diff --git a/libs/wechatpad_api/LICENSE b/src/langbot/libs/wechatpad_api/LICENSE similarity index 100% rename from libs/wechatpad_api/LICENSE rename to src/langbot/libs/wechatpad_api/LICENSE diff --git a/libs/wechatpad_api/README.md b/src/langbot/libs/wechatpad_api/README.md similarity index 100% rename from libs/wechatpad_api/README.md rename to src/langbot/libs/wechatpad_api/README.md diff --git a/libs/wechatpad_api/__init__.py b/src/langbot/libs/wechatpad_api/__init__.py similarity index 100% rename from libs/wechatpad_api/__init__.py rename to src/langbot/libs/wechatpad_api/__init__.py diff --git a/libs/wechatpad_api/api/__init__.py b/src/langbot/libs/wechatpad_api/api/__init__.py similarity index 100% rename from libs/wechatpad_api/api/__init__.py rename to src/langbot/libs/wechatpad_api/api/__init__.py diff --git a/libs/wechatpad_api/api/chatroom.py b/src/langbot/libs/wechatpad_api/api/chatroom.py similarity index 84% rename from libs/wechatpad_api/api/chatroom.py rename to src/langbot/libs/wechatpad_api/api/chatroom.py index 2d9281a2..63360a23 100644 --- a/libs/wechatpad_api/api/chatroom.py +++ b/src/langbot/libs/wechatpad_api/api/chatroom.py @@ -1,4 +1,4 @@ -from libs.wechatpad_api.util.http_util import post_json +from langbot.libs.wechatpad_api.util.http_util import post_json class ChatRoomApi: diff --git a/libs/wechatpad_api/api/downloadpai.py b/src/langbot/libs/wechatpad_api/api/downloadpai.py similarity index 94% rename from libs/wechatpad_api/api/downloadpai.py rename to src/langbot/libs/wechatpad_api/api/downloadpai.py index 2d45fac6..3fbdb624 100644 --- a/libs/wechatpad_api/api/downloadpai.py +++ b/src/langbot/libs/wechatpad_api/api/downloadpai.py @@ -1,4 +1,4 @@ -from libs.wechatpad_api.util.http_util import post_json +from langbot.libs.wechatpad_api.util.http_util import post_json import httpx import base64 diff --git a/libs/wechatpad_api/api/friend.py b/src/langbot/libs/wechatpad_api/api/friend.py similarity index 100% rename from libs/wechatpad_api/api/friend.py rename to src/langbot/libs/wechatpad_api/api/friend.py diff --git a/libs/wechatpad_api/api/login.py b/src/langbot/libs/wechatpad_api/api/login.py similarity index 96% rename from libs/wechatpad_api/api/login.py rename to src/langbot/libs/wechatpad_api/api/login.py index 4aa4ae8d..23e89b88 100644 --- a/libs/wechatpad_api/api/login.py +++ b/src/langbot/libs/wechatpad_api/api/login.py @@ -1,4 +1,4 @@ -from libs.wechatpad_api.util.http_util import post_json, get_json +from langbot.libs.wechatpad_api.util.http_util import post_json, get_json class LoginApi: diff --git a/libs/wechatpad_api/api/message.py b/src/langbot/libs/wechatpad_api/api/message.py similarity index 97% rename from libs/wechatpad_api/api/message.py rename to src/langbot/libs/wechatpad_api/api/message.py index cca76313..52c7bdb5 100644 --- a/libs/wechatpad_api/api/message.py +++ b/src/langbot/libs/wechatpad_api/api/message.py @@ -1,4 +1,4 @@ -from libs.wechatpad_api.util.http_util import post_json +from langbot.libs.wechatpad_api.util.http_util import post_json class MessageApi: diff --git a/libs/wechatpad_api/api/user.py b/src/langbot/libs/wechatpad_api/api/user.py similarity index 91% rename from libs/wechatpad_api/api/user.py rename to src/langbot/libs/wechatpad_api/api/user.py index d2187c7c..0725b9d3 100644 --- a/libs/wechatpad_api/api/user.py +++ b/src/langbot/libs/wechatpad_api/api/user.py @@ -1,4 +1,4 @@ -from libs.wechatpad_api.util.http_util import post_json, async_request, get_json +from langbot.libs.wechatpad_api.util.http_util import post_json, async_request, get_json class UserApi: diff --git a/libs/wechatpad_api/client.py b/src/langbot/libs/wechatpad_api/client.py similarity index 89% rename from libs/wechatpad_api/client.py rename to src/langbot/libs/wechatpad_api/client.py index 5e699d03..bb9b2f54 100644 --- a/libs/wechatpad_api/client.py +++ b/src/langbot/libs/wechatpad_api/client.py @@ -1,9 +1,9 @@ -from libs.wechatpad_api.api.login import LoginApi -from libs.wechatpad_api.api.friend import FriendApi -from libs.wechatpad_api.api.message import MessageApi -from libs.wechatpad_api.api.user import UserApi -from libs.wechatpad_api.api.downloadpai import DownloadApi -from libs.wechatpad_api.api.chatroom import ChatRoomApi +from langbot.libs.wechatpad_api.api.login import LoginApi +from langbot.libs.wechatpad_api.api.friend import FriendApi +from langbot.libs.wechatpad_api.api.message import MessageApi +from langbot.libs.wechatpad_api.api.user import UserApi +from langbot.libs.wechatpad_api.api.downloadpai import DownloadApi +from langbot.libs.wechatpad_api.api.chatroom import ChatRoomApi class WeChatPadClient: diff --git a/libs/wechatpad_api/util/__init__.py b/src/langbot/libs/wechatpad_api/util/__init__.py similarity index 100% rename from libs/wechatpad_api/util/__init__.py rename to src/langbot/libs/wechatpad_api/util/__init__.py diff --git a/libs/wechatpad_api/util/http_util.py b/src/langbot/libs/wechatpad_api/util/http_util.py similarity index 100% rename from libs/wechatpad_api/util/http_util.py rename to src/langbot/libs/wechatpad_api/util/http_util.py diff --git a/libs/wechatpad_api/util/terminal_printer.py b/src/langbot/libs/wechatpad_api/util/terminal_printer.py similarity index 100% rename from libs/wechatpad_api/util/terminal_printer.py rename to src/langbot/libs/wechatpad_api/util/terminal_printer.py diff --git a/libs/wecom_ai_bot_api/WXBizMsgCrypt3.py b/src/langbot/libs/wecom_ai_bot_api/WXBizMsgCrypt3.py similarity index 99% rename from libs/wecom_ai_bot_api/WXBizMsgCrypt3.py rename to src/langbot/libs/wecom_ai_bot_api/WXBizMsgCrypt3.py index 96e9367f..bd8250da 100644 --- a/libs/wecom_ai_bot_api/WXBizMsgCrypt3.py +++ b/src/langbot/libs/wecom_ai_bot_api/WXBizMsgCrypt3.py @@ -16,7 +16,7 @@ import struct from Crypto.Cipher import AES import xml.etree.cElementTree as ET import socket -from libs.wecom_ai_bot_api import ierror +from langbot.libs.wecom_ai_bot_api import ierror """ diff --git a/libs/wecom_ai_bot_api/api.py b/src/langbot/libs/wecom_ai_bot_api/api.py similarity index 93% rename from libs/wecom_ai_bot_api/api.py rename to src/langbot/libs/wecom_ai_bot_api/api.py index b20c6ed3..0620791b 100644 --- a/libs/wecom_ai_bot_api/api.py +++ b/src/langbot/libs/wecom_ai_bot_api/api.py @@ -13,9 +13,9 @@ import httpx from Crypto.Cipher import AES from quart import Quart, request, Response, jsonify -from libs.wecom_ai_bot_api import wecombotevent -from libs.wecom_ai_bot_api.WXBizMsgCrypt3 import WXBizMsgCrypt -from pkg.platform.logger import EventLogger +from langbot.libs.wecom_ai_bot_api import wecombotevent +from langbot.libs.wecom_ai_bot_api.WXBizMsgCrypt3 import WXBizMsgCrypt +from langbot.pkg.platform.logger import EventLogger @dataclass @@ -219,10 +219,7 @@ class WecomBotClient: self.ReceiveId = '' self.app = Quart(__name__) self.app.add_url_rule( - '/callback/command', - 'handle_callback', - self.handle_callback_request, - methods=['POST', 'GET'] + '/callback/command', 'handle_callback', self.handle_callback_request, methods=['POST', 'GET'] ) self._message_handlers = { 'example': [], @@ -420,7 +417,7 @@ class WecomBotClient: await self.logger.error("请求体中缺少 'encrypt' 字段") return Response('Bad Request', status=400) - xml_post_data = f"" + xml_post_data = f'' ret, decrypted_xml = self.wxcpt.DecryptMsg(xml_post_data, msg_signature, timestamp, nonce) if ret != 0: await self.logger.error('解密失败') @@ -458,7 +455,7 @@ class WecomBotClient: picurl = item.get('image', {}).get('url') if texts: - message_data['content'] = "".join(texts) # 拼接所有 text + message_data['content'] = ''.join(texts) # 拼接所有 text if picurl: base64 = await self.download_url_to_base64(picurl, self.EnCodingAESKey) message_data['picurl'] = base64 # 只保留第一个 image @@ -466,7 +463,9 @@ class WecomBotClient: # Extract user information from_info = msg_json.get('from', {}) message_data['userid'] = from_info.get('userid', '') - message_data['username'] = from_info.get('alias', '') or from_info.get('name', '') or from_info.get('userid', '') + message_data['username'] = ( + from_info.get('alias', '') or from_info.get('name', '') or from_info.get('userid', '') + ) # Extract chat/group information if msg_json.get('chattype', '') == 'group': @@ -555,7 +554,7 @@ class WecomBotClient: encrypted_bytes = response.content - aes_key = base64.b64decode(encoding_aes_key + "=") # base64 补齐 + aes_key = base64.b64decode(encoding_aes_key + '=') # base64 补齐 iv = aes_key[:16] cipher = AES.new(aes_key, AES.MODE_CBC, iv) @@ -564,22 +563,22 @@ class WecomBotClient: pad_len = decrypted[-1] decrypted = decrypted[:-pad_len] - if decrypted.startswith(b"\xff\xd8"): # JPEG - mime_type = "image/jpeg" - elif decrypted.startswith(b"\x89PNG"): # PNG - mime_type = "image/png" - elif decrypted.startswith((b"GIF87a", b"GIF89a")): # GIF - mime_type = "image/gif" - elif decrypted.startswith(b"BM"): # BMP - mime_type = "image/bmp" - elif decrypted.startswith(b"II*\x00") or decrypted.startswith(b"MM\x00*"): # TIFF - mime_type = "image/tiff" + if decrypted.startswith(b'\xff\xd8'): # JPEG + mime_type = 'image/jpeg' + elif decrypted.startswith(b'\x89PNG'): # PNG + mime_type = 'image/png' + elif decrypted.startswith((b'GIF87a', b'GIF89a')): # GIF + mime_type = 'image/gif' + elif decrypted.startswith(b'BM'): # BMP + mime_type = 'image/bmp' + elif decrypted.startswith(b'II*\x00') or decrypted.startswith(b'MM\x00*'): # TIFF + mime_type = 'image/tiff' else: - mime_type = "application/octet-stream" + mime_type = 'application/octet-stream' # 转 base64 - base64_str = base64.b64encode(decrypted).decode("utf-8") - return f"data:{mime_type};base64,{base64_str}" + base64_str = base64.b64encode(decrypted).decode('utf-8') + return f'data:{mime_type};base64,{base64_str}' async def run_task(self, host: str, port: int, *args, **kwargs): """ diff --git a/libs/wecom_ai_bot_api/ierror.py b/src/langbot/libs/wecom_ai_bot_api/ierror.py similarity index 100% rename from libs/wecom_ai_bot_api/ierror.py rename to src/langbot/libs/wecom_ai_bot_api/ierror.py diff --git a/libs/wecom_ai_bot_api/wecombotevent.py b/src/langbot/libs/wecom_ai_bot_api/wecombotevent.py similarity index 88% rename from libs/wecom_ai_bot_api/wecombotevent.py rename to src/langbot/libs/wecom_ai_bot_api/wecombotevent.py index 099c58bc..b2f02831 100644 --- a/libs/wecom_ai_bot_api/wecombotevent.py +++ b/src/langbot/libs/wecom_ai_bot_api/wecombotevent.py @@ -29,7 +29,12 @@ class WecomBotEvent(dict): """ 用户名称 """ - return self.get('username', '') or self.get('from', {}).get('alias', '') or self.get('from', {}).get('name', '') or self.userid + return ( + self.get('username', '') + or self.get('from', {}).get('alias', '') + or self.get('from', {}).get('name', '') + or self.userid + ) @property def chatname(self) -> str: @@ -65,7 +70,7 @@ class WecomBotEvent(dict): 消息id """ return self.get('msgid', '') - + @property def ai_bot_id(self) -> str: """ diff --git a/libs/wecom_api/WXBizMsgCrypt3.py b/src/langbot/libs/wecom_api/WXBizMsgCrypt3.py similarity index 100% rename from libs/wecom_api/WXBizMsgCrypt3.py rename to src/langbot/libs/wecom_api/WXBizMsgCrypt3.py diff --git a/libs/wecom_api/__init__.py b/src/langbot/libs/wecom_api/__init__.py similarity index 100% rename from libs/wecom_api/__init__.py rename to src/langbot/libs/wecom_api/__init__.py diff --git a/libs/wecom_api/api.py b/src/langbot/libs/wecom_api/api.py similarity index 99% rename from libs/wecom_api/api.py rename to src/langbot/libs/wecom_api/api.py index d9cb7726..352a550c 100644 --- a/libs/wecom_api/api.py +++ b/src/langbot/libs/wecom_api/api.py @@ -340,4 +340,3 @@ class WecomClient: async def get_media_id(self, image: platform_message.Image): media_id = await self.upload_to_work(image=image) return media_id - diff --git a/libs/wecom_api/ierror.py b/src/langbot/libs/wecom_api/ierror.py similarity index 100% rename from libs/wecom_api/ierror.py rename to src/langbot/libs/wecom_api/ierror.py diff --git a/libs/wecom_api/wecomevent.py b/src/langbot/libs/wecom_api/wecomevent.py similarity index 100% rename from libs/wecom_api/wecomevent.py rename to src/langbot/libs/wecom_api/wecomevent.py diff --git a/libs/wecom_customer_service_api/__init__.py b/src/langbot/libs/wecom_customer_service_api/__init__.py similarity index 100% rename from libs/wecom_customer_service_api/__init__.py rename to src/langbot/libs/wecom_customer_service_api/__init__.py diff --git a/libs/wecom_customer_service_api/api.py b/src/langbot/libs/wecom_customer_service_api/api.py similarity index 100% rename from libs/wecom_customer_service_api/api.py rename to src/langbot/libs/wecom_customer_service_api/api.py diff --git a/libs/wecom_customer_service_api/wecomcsevent.py b/src/langbot/libs/wecom_customer_service_api/wecomcsevent.py similarity index 100% rename from libs/wecom_customer_service_api/wecomcsevent.py rename to src/langbot/libs/wecom_customer_service_api/wecomcsevent.py diff --git a/pkg/__init__.py b/src/langbot/pkg/__init__.py similarity index 100% rename from pkg/__init__.py rename to src/langbot/pkg/__init__.py diff --git a/pkg/api/__init__.py b/src/langbot/pkg/api/__init__.py similarity index 100% rename from pkg/api/__init__.py rename to src/langbot/pkg/api/__init__.py diff --git a/pkg/api/http/__init__.py b/src/langbot/pkg/api/http/__init__.py similarity index 100% rename from pkg/api/http/__init__.py rename to src/langbot/pkg/api/http/__init__.py diff --git a/pkg/api/http/controller/__init__.py b/src/langbot/pkg/api/http/controller/__init__.py similarity index 100% rename from pkg/api/http/controller/__init__.py rename to src/langbot/pkg/api/http/controller/__init__.py diff --git a/pkg/api/http/controller/group.py b/src/langbot/pkg/api/http/controller/group.py similarity index 97% rename from pkg/api/http/controller/group.py rename to src/langbot/pkg/api/http/controller/group.py index 8a61cefc..2ed55187 100644 --- a/pkg/api/http/controller/group.py +++ b/src/langbot/pkg/api/http/controller/group.py @@ -110,7 +110,7 @@ class RouterGroup(abc.ABC): elif auth_type == AuthType.USER_TOKEN_OR_API_KEY: # Try API key first (check X-API-Key header) api_key = quart.request.headers.get('X-API-Key', '') - + if api_key: # API key authentication try: @@ -124,7 +124,9 @@ class RouterGroup(abc.ABC): token = quart.request.headers.get('Authorization', '').replace('Bearer ', '') if not token: - return self.http_status(401, -1, 'No valid authentication provided (user token or API key required)') + return self.http_status( + 401, -1, 'No valid authentication provided (user token or API key required)' + ) try: user_email = await self.ap.user_service.verify_jwt_token(token) diff --git a/pkg/api/http/controller/groups/__init__.py b/src/langbot/pkg/api/http/controller/groups/__init__.py similarity index 100% rename from pkg/api/http/controller/groups/__init__.py rename to src/langbot/pkg/api/http/controller/groups/__init__.py diff --git a/pkg/api/http/controller/groups/apikeys.py b/src/langbot/pkg/api/http/controller/groups/apikeys.py similarity index 100% rename from pkg/api/http/controller/groups/apikeys.py rename to src/langbot/pkg/api/http/controller/groups/apikeys.py diff --git a/pkg/api/http/controller/groups/files.py b/src/langbot/pkg/api/http/controller/groups/files.py similarity index 100% rename from pkg/api/http/controller/groups/files.py rename to src/langbot/pkg/api/http/controller/groups/files.py diff --git a/pkg/api/http/controller/groups/knowledge/__init__.py b/src/langbot/pkg/api/http/controller/groups/knowledge/__init__.py similarity index 100% rename from pkg/api/http/controller/groups/knowledge/__init__.py rename to src/langbot/pkg/api/http/controller/groups/knowledge/__init__.py diff --git a/pkg/api/http/controller/groups/knowledge/base.py b/src/langbot/pkg/api/http/controller/groups/knowledge/base.py similarity index 100% rename from pkg/api/http/controller/groups/knowledge/base.py rename to src/langbot/pkg/api/http/controller/groups/knowledge/base.py diff --git a/pkg/api/http/controller/groups/logs.py b/src/langbot/pkg/api/http/controller/groups/logs.py similarity index 100% rename from pkg/api/http/controller/groups/logs.py rename to src/langbot/pkg/api/http/controller/groups/logs.py diff --git a/pkg/api/http/controller/groups/pipelines/__init__.py b/src/langbot/pkg/api/http/controller/groups/pipelines/__init__.py similarity index 100% rename from pkg/api/http/controller/groups/pipelines/__init__.py rename to src/langbot/pkg/api/http/controller/groups/pipelines/__init__.py diff --git a/pkg/api/http/controller/groups/pipelines/pipelines.py b/src/langbot/pkg/api/http/controller/groups/pipelines/pipelines.py similarity index 92% rename from pkg/api/http/controller/groups/pipelines/pipelines.py rename to src/langbot/pkg/api/http/controller/groups/pipelines/pipelines.py index 8285fa85..924b68d4 100644 --- a/pkg/api/http/controller/groups/pipelines/pipelines.py +++ b/src/langbot/pkg/api/http/controller/groups/pipelines/pipelines.py @@ -27,7 +27,9 @@ class PipelinesRouterGroup(group.RouterGroup): async def _() -> str: return self.success(data={'configs': await self.ap.pipeline_service.get_pipeline_metadata()}) - @self.route('/', methods=['GET', 'PUT', 'DELETE'], auth_type=group.AuthType.USER_TOKEN_OR_API_KEY) + @self.route( + '/', methods=['GET', 'PUT', 'DELETE'], auth_type=group.AuthType.USER_TOKEN_OR_API_KEY + ) async def _(pipeline_uuid: str) -> str: if quart.request.method == 'GET': pipeline = await self.ap.pipeline_service.get_pipeline(pipeline_uuid) @@ -47,7 +49,9 @@ class PipelinesRouterGroup(group.RouterGroup): return self.success() - @self.route('//extensions', methods=['GET', 'PUT'], auth_type=group.AuthType.USER_TOKEN_OR_API_KEY) + @self.route( + '//extensions', methods=['GET', 'PUT'], auth_type=group.AuthType.USER_TOKEN_OR_API_KEY + ) async def _(pipeline_uuid: str) -> str: if quart.request.method == 'GET': # Get current extensions and available plugins diff --git a/pkg/api/http/controller/groups/pipelines/webchat.py b/src/langbot/pkg/api/http/controller/groups/pipelines/webchat.py similarity index 100% rename from pkg/api/http/controller/groups/pipelines/webchat.py rename to src/langbot/pkg/api/http/controller/groups/pipelines/webchat.py diff --git a/pkg/api/http/controller/groups/platform/__init__.py b/src/langbot/pkg/api/http/controller/groups/platform/__init__.py similarity index 100% rename from pkg/api/http/controller/groups/platform/__init__.py rename to src/langbot/pkg/api/http/controller/groups/platform/__init__.py diff --git a/pkg/api/http/controller/groups/platform/adapters.py b/src/langbot/pkg/api/http/controller/groups/platform/adapters.py similarity index 85% rename from pkg/api/http/controller/groups/platform/adapters.py rename to src/langbot/pkg/api/http/controller/groups/platform/adapters.py index 4136791c..b46e5263 100644 --- a/pkg/api/http/controller/groups/platform/adapters.py +++ b/src/langbot/pkg/api/http/controller/groups/platform/adapters.py @@ -1,6 +1,7 @@ import quart - +import mimetypes from ... import group +from langbot.pkg.utils import importutil @group.group_class('adapters', '/api/v1/platform/adapters') @@ -31,4 +32,6 @@ class AdaptersRouterGroup(group.RouterGroup): if icon_path is None: return self.http_status(404, -1, 'icon not found') - return await quart.send_file(icon_path) + return quart.Response( + importutil.read_resource_file_bytes(icon_path), mimetype=mimetypes.guess_type(icon_path)[0] + ) diff --git a/pkg/api/http/controller/groups/platform/bots.py b/src/langbot/pkg/api/http/controller/groups/platform/bots.py similarity index 100% rename from pkg/api/http/controller/groups/platform/bots.py rename to src/langbot/pkg/api/http/controller/groups/platform/bots.py diff --git a/pkg/api/http/controller/groups/plugins.py b/src/langbot/pkg/api/http/controller/groups/plugins.py similarity index 100% rename from pkg/api/http/controller/groups/plugins.py rename to src/langbot/pkg/api/http/controller/groups/plugins.py diff --git a/pkg/api/http/controller/groups/provider/__init__.py b/src/langbot/pkg/api/http/controller/groups/provider/__init__.py similarity index 100% rename from pkg/api/http/controller/groups/provider/__init__.py rename to src/langbot/pkg/api/http/controller/groups/provider/__init__.py diff --git a/pkg/api/http/controller/groups/provider/models.py b/src/langbot/pkg/api/http/controller/groups/provider/models.py similarity index 100% rename from pkg/api/http/controller/groups/provider/models.py rename to src/langbot/pkg/api/http/controller/groups/provider/models.py diff --git a/pkg/api/http/controller/groups/provider/requesters.py b/src/langbot/pkg/api/http/controller/groups/provider/requesters.py similarity index 86% rename from pkg/api/http/controller/groups/provider/requesters.py rename to src/langbot/pkg/api/http/controller/groups/provider/requesters.py index af9e1540..268ed11d 100644 --- a/pkg/api/http/controller/groups/provider/requesters.py +++ b/src/langbot/pkg/api/http/controller/groups/provider/requesters.py @@ -1,6 +1,8 @@ import quart +import mimetypes from ... import group +from langbot.pkg.utils import importutil @group.group_class('provider/requesters', '/api/v1/provider/requesters') @@ -32,4 +34,6 @@ class RequestersRouterGroup(group.RouterGroup): if icon_path is None: return self.http_status(404, -1, 'icon not found') - return await quart.send_file(icon_path) + return quart.Response( + importutil.read_resource_file_bytes(icon_path), mimetype=mimetypes.guess_type(icon_path)[0] + ) diff --git a/pkg/api/http/controller/groups/resources/__init__.py b/src/langbot/pkg/api/http/controller/groups/resources/__init__.py similarity index 100% rename from pkg/api/http/controller/groups/resources/__init__.py rename to src/langbot/pkg/api/http/controller/groups/resources/__init__.py diff --git a/pkg/api/http/controller/groups/resources/mcp.py b/src/langbot/pkg/api/http/controller/groups/resources/mcp.py similarity index 100% rename from pkg/api/http/controller/groups/resources/mcp.py rename to src/langbot/pkg/api/http/controller/groups/resources/mcp.py diff --git a/pkg/api/http/controller/groups/stats.py b/src/langbot/pkg/api/http/controller/groups/stats.py similarity index 100% rename from pkg/api/http/controller/groups/stats.py rename to src/langbot/pkg/api/http/controller/groups/stats.py diff --git a/pkg/api/http/controller/groups/system.py b/src/langbot/pkg/api/http/controller/groups/system.py similarity index 100% rename from pkg/api/http/controller/groups/system.py rename to src/langbot/pkg/api/http/controller/groups/system.py diff --git a/pkg/api/http/controller/groups/user.py b/src/langbot/pkg/api/http/controller/groups/user.py similarity index 100% rename from pkg/api/http/controller/groups/user.py rename to src/langbot/pkg/api/http/controller/groups/user.py diff --git a/pkg/api/http/controller/groups/webhooks.py b/src/langbot/pkg/api/http/controller/groups/webhooks.py similarity index 100% rename from pkg/api/http/controller/groups/webhooks.py rename to src/langbot/pkg/api/http/controller/groups/webhooks.py diff --git a/pkg/api/http/controller/main.py b/src/langbot/pkg/api/http/controller/main.py similarity index 98% rename from pkg/api/http/controller/main.py rename to src/langbot/pkg/api/http/controller/main.py index ca12f4bb..1541a25e 100644 --- a/pkg/api/http/controller/main.py +++ b/src/langbot/pkg/api/http/controller/main.py @@ -86,7 +86,9 @@ class HTTPController: ginst = g(self.ap, self.quart_app) await ginst.initialize() - frontend_path = 'web/out' + from ....utils import paths + + frontend_path = paths.get_frontend_path() @self.quart_app.route('/') async def index(): diff --git a/pkg/api/http/service/__init__.py b/src/langbot/pkg/api/http/service/__init__.py similarity index 100% rename from pkg/api/http/service/__init__.py rename to src/langbot/pkg/api/http/service/__init__.py diff --git a/pkg/api/http/service/apikey.py b/src/langbot/pkg/api/http/service/apikey.py similarity index 94% rename from pkg/api/http/service/apikey.py rename to src/langbot/pkg/api/http/service/apikey.py index 1a8b2892..c46b5608 100644 --- a/pkg/api/http/service/apikey.py +++ b/src/langbot/pkg/api/http/service/apikey.py @@ -61,9 +61,7 @@ class ApiKeyService: async def delete_api_key(self, key_id: int) -> None: """Delete an API key""" - await self.ap.persistence_mgr.execute_async( - sqlalchemy.delete(apikey.ApiKey).where(apikey.ApiKey.id == key_id) - ) + await self.ap.persistence_mgr.execute_async(sqlalchemy.delete(apikey.ApiKey).where(apikey.ApiKey.id == key_id)) async def update_api_key(self, key_id: int, name: str = None, description: str = None) -> None: """Update an API key's metadata (name, description)""" diff --git a/pkg/api/http/service/bot.py b/src/langbot/pkg/api/http/service/bot.py similarity index 100% rename from pkg/api/http/service/bot.py rename to src/langbot/pkg/api/http/service/bot.py diff --git a/pkg/api/http/service/knowledge.py b/src/langbot/pkg/api/http/service/knowledge.py similarity index 100% rename from pkg/api/http/service/knowledge.py rename to src/langbot/pkg/api/http/service/knowledge.py diff --git a/pkg/api/http/service/mcp.py b/src/langbot/pkg/api/http/service/mcp.py similarity index 98% rename from pkg/api/http/service/mcp.py rename to src/langbot/pkg/api/http/service/mcp.py index 328b9c20..a1b034d0 100644 --- a/pkg/api/http/service/mcp.py +++ b/src/langbot/pkg/api/http/service/mcp.py @@ -84,13 +84,11 @@ class MCPService: new_enable = server_data.get('enable', False) need_remove = old_server_name and old_server_name in self.ap.tool_mgr.mcp_tool_loader.sessions - need_start = new_enable - if old_enable and not new_enable: if need_remove: await self.ap.tool_mgr.mcp_tool_loader.remove_mcp_server(old_server_name) - + elif not old_enable and new_enable: result = await self.ap.persistence_mgr.execute_async( sqlalchemy.select(persistence_mcp.MCPServer).where(persistence_mcp.MCPServer.uuid == server_uuid) @@ -100,7 +98,7 @@ class MCPService: server_config = self.ap.persistence_mgr.serialize_model(persistence_mcp.MCPServer, updated_server) task = asyncio.create_task(self.ap.tool_mgr.mcp_tool_loader.host_mcp_server(server_config)) self.ap.tool_mgr.mcp_tool_loader._hosted_mcp_tasks.append(task) - + elif old_enable and new_enable: if need_remove: await self.ap.tool_mgr.mcp_tool_loader.remove_mcp_server(old_server_name) @@ -112,7 +110,6 @@ class MCPService: server_config = self.ap.persistence_mgr.serialize_model(persistence_mcp.MCPServer, updated_server) task = asyncio.create_task(self.ap.tool_mgr.mcp_tool_loader.host_mcp_server(server_config)) self.ap.tool_mgr.mcp_tool_loader._hosted_mcp_tasks.append(task) - async def delete_mcp_server(self, server_uuid: str) -> None: result = await self.ap.persistence_mgr.execute_async( diff --git a/pkg/api/http/service/model.py b/src/langbot/pkg/api/http/service/model.py similarity index 100% rename from pkg/api/http/service/model.py rename to src/langbot/pkg/api/http/service/model.py diff --git a/pkg/api/http/service/pipeline.py b/src/langbot/pkg/api/http/service/pipeline.py similarity index 90% rename from pkg/api/http/service/pipeline.py rename to src/langbot/pkg/api/http/service/pipeline.py index 2e00a74d..62e0879f 100644 --- a/pkg/api/http/service/pipeline.py +++ b/src/langbot/pkg/api/http/service/pipeline.py @@ -30,12 +30,12 @@ class PipelineService: def __init__(self, ap: app.Application) -> None: self.ap = ap - async def get_pipeline_metadata(self) -> dict: + async def get_pipeline_metadata(self) -> list[dict]: return [ - self.ap.pipeline_config_meta_trigger.data, - self.ap.pipeline_config_meta_safety.data, - self.ap.pipeline_config_meta_ai.data, - self.ap.pipeline_config_meta_output.data, + self.ap.pipeline_config_meta_trigger, + self.ap.pipeline_config_meta_safety, + self.ap.pipeline_config_meta_ai, + self.ap.pipeline_config_meta_output, ] async def get_pipelines(self, sort_by: str = 'created_at', sort_order: str = 'DESC') -> list[dict]: @@ -74,11 +74,16 @@ class PipelineService: return self.ap.persistence_mgr.serialize_model(persistence_pipeline.LegacyPipeline, pipeline) async def create_pipeline(self, pipeline_data: dict, default: bool = False) -> str: + from ....utils import paths as path_utils + pipeline_data['uuid'] = str(uuid.uuid4()) pipeline_data['for_version'] = self.ap.ver_mgr.get_current_version() pipeline_data['stages'] = default_stage_order.copy() pipeline_data['is_default'] = default - pipeline_data['config'] = json.load(open('templates/default-pipeline-config.json', 'r', encoding='utf-8')) + + template_path = path_utils.get_resource_path('templates/default-pipeline-config.json') + with open(template_path, 'r', encoding='utf-8') as f: + pipeline_data['config'] = json.load(f) await self.ap.persistence_mgr.execute_async( sqlalchemy.insert(persistence_pipeline.LegacyPipeline).values(**pipeline_data) @@ -137,7 +142,9 @@ class PipelineService: ) await self.ap.pipeline_mgr.remove_pipeline(pipeline_uuid) - async def update_pipeline_extensions(self, pipeline_uuid: str, bound_plugins: list[dict], bound_mcp_servers: list[str] = None) -> None: + async def update_pipeline_extensions( + self, pipeline_uuid: str, bound_plugins: list[dict], bound_mcp_servers: list[str] = None + ) -> None: """Update the bound plugins and MCP servers for a pipeline""" # Get current pipeline result = await self.ap.persistence_mgr.execute_async( @@ -145,23 +152,23 @@ class PipelineService: persistence_pipeline.LegacyPipeline.uuid == pipeline_uuid ) ) - + pipeline = result.first() if pipeline is None: raise ValueError(f'Pipeline {pipeline_uuid} not found') - + # Update extensions_preferences extensions_preferences = pipeline.extensions_preferences or {} extensions_preferences['plugins'] = bound_plugins if bound_mcp_servers is not None: extensions_preferences['mcp_servers'] = bound_mcp_servers - + await self.ap.persistence_mgr.execute_async( sqlalchemy.update(persistence_pipeline.LegacyPipeline) .where(persistence_pipeline.LegacyPipeline.uuid == pipeline_uuid) .values(extensions_preferences=extensions_preferences) ) - + # Reload pipeline to apply changes await self.ap.pipeline_mgr.remove_pipeline(pipeline_uuid) pipeline = await self.get_pipeline(pipeline_uuid) diff --git a/pkg/api/http/service/user.py b/src/langbot/pkg/api/http/service/user.py similarity index 100% rename from pkg/api/http/service/user.py rename to src/langbot/pkg/api/http/service/user.py diff --git a/pkg/api/http/service/webhook.py b/src/langbot/pkg/api/http/service/webhook.py similarity index 100% rename from pkg/api/http/service/webhook.py rename to src/langbot/pkg/api/http/service/webhook.py diff --git a/pkg/command/__init__.py b/src/langbot/pkg/command/__init__.py similarity index 100% rename from pkg/command/__init__.py rename to src/langbot/pkg/command/__init__.py diff --git a/pkg/command/cmdmgr.py b/src/langbot/pkg/command/cmdmgr.py similarity index 100% rename from pkg/command/cmdmgr.py rename to src/langbot/pkg/command/cmdmgr.py diff --git a/pkg/command/operator.py b/src/langbot/pkg/command/operator.py similarity index 100% rename from pkg/command/operator.py rename to src/langbot/pkg/command/operator.py diff --git a/pkg/command/operators/__init__.py b/src/langbot/pkg/command/operators/__init__.py similarity index 100% rename from pkg/command/operators/__init__.py rename to src/langbot/pkg/command/operators/__init__.py diff --git a/pkg/command/operators/delc.py b/src/langbot/pkg/command/operators/delc.py similarity index 100% rename from pkg/command/operators/delc.py rename to src/langbot/pkg/command/operators/delc.py diff --git a/pkg/command/operators/last.py b/src/langbot/pkg/command/operators/last.py similarity index 100% rename from pkg/command/operators/last.py rename to src/langbot/pkg/command/operators/last.py diff --git a/pkg/command/operators/list.py b/src/langbot/pkg/command/operators/list.py similarity index 100% rename from pkg/command/operators/list.py rename to src/langbot/pkg/command/operators/list.py diff --git a/pkg/command/operators/next.py b/src/langbot/pkg/command/operators/next.py similarity index 100% rename from pkg/command/operators/next.py rename to src/langbot/pkg/command/operators/next.py diff --git a/pkg/command/operators/prompt.py b/src/langbot/pkg/command/operators/prompt.py similarity index 100% rename from pkg/command/operators/prompt.py rename to src/langbot/pkg/command/operators/prompt.py diff --git a/pkg/command/operators/resend.py b/src/langbot/pkg/command/operators/resend.py similarity index 100% rename from pkg/command/operators/resend.py rename to src/langbot/pkg/command/operators/resend.py diff --git a/pkg/config/__init__.py b/src/langbot/pkg/config/__init__.py similarity index 100% rename from pkg/config/__init__.py rename to src/langbot/pkg/config/__init__.py diff --git a/pkg/config/impls/__init__.py b/src/langbot/pkg/config/impls/__init__.py similarity index 100% rename from pkg/config/impls/__init__.py rename to src/langbot/pkg/config/impls/__init__.py diff --git a/pkg/config/impls/json.py b/src/langbot/pkg/config/impls/json.py similarity index 65% rename from pkg/config/impls/json.py rename to src/langbot/pkg/config/impls/json.py index 44b4843c..00683415 100644 --- a/pkg/config/impls/json.py +++ b/src/langbot/pkg/config/impls/json.py @@ -1,8 +1,8 @@ import os -import shutil import json +import importlib.resources as resources -from .. import model as file_model +from langbot.pkg.config import model as file_model class JSONConfigFile(file_model.ConfigFile): @@ -11,19 +11,29 @@ class JSONConfigFile(file_model.ConfigFile): def __init__( self, config_file_name: str, - template_file_name: str = None, + template_resource_name: str = None, template_data: dict = None, ) -> None: self.config_file_name = config_file_name - self.template_file_name = template_file_name + self.template_resource_name = template_resource_name self.template_data = template_data def exists(self) -> bool: return os.path.exists(self.config_file_name) + async def get_template_file_str(self) -> str: + if self.template_resource_name is None: + return None + + with ( + resources.files('langbot.templates').joinpath(self.template_resource_name).open('r', encoding='utf-8') as f + ): + return f.read() + async def create(self): - if self.template_file_name is not None: - shutil.copyfile(self.template_file_name, self.config_file_name) + if await self.get_template_file_str() is not None: + with open(self.config_file_name, 'w', encoding='utf-8') as f: + f.write(await self.get_template_file_str()) elif self.template_data is not None: with open(self.config_file_name, 'w', encoding='utf-8') as f: json.dump(self.template_data, f, indent=4, ensure_ascii=False) @@ -34,9 +44,10 @@ class JSONConfigFile(file_model.ConfigFile): if not self.exists(): await self.create() - if self.template_file_name is not None: - with open(self.template_file_name, 'r', encoding='utf-8') as f: - self.template_data = json.load(f) + template_file_str = await self.get_template_file_str() + + if template_file_str is not None: + self.template_data = json.loads(template_file_str) with open(self.config_file_name, 'r', encoding='utf-8') as f: try: diff --git a/pkg/config/impls/pymodule.py b/src/langbot/pkg/config/impls/pymodule.py similarity index 100% rename from pkg/config/impls/pymodule.py rename to src/langbot/pkg/config/impls/pymodule.py diff --git a/pkg/config/impls/yaml.py b/src/langbot/pkg/config/impls/yaml.py similarity index 64% rename from pkg/config/impls/yaml.py rename to src/langbot/pkg/config/impls/yaml.py index 0d69ef9e..d9dc4bc2 100644 --- a/pkg/config/impls/yaml.py +++ b/src/langbot/pkg/config/impls/yaml.py @@ -1,8 +1,8 @@ import os -import shutil import yaml +import importlib.resources as resources -from .. import model as file_model +from langbot.pkg.config import model as file_model class YAMLConfigFile(file_model.ConfigFile): @@ -11,19 +11,29 @@ class YAMLConfigFile(file_model.ConfigFile): def __init__( self, config_file_name: str, - template_file_name: str = None, + template_resource_name: str = None, template_data: dict = None, ) -> None: self.config_file_name = config_file_name - self.template_file_name = template_file_name + self.template_resource_name = template_resource_name self.template_data = template_data def exists(self) -> bool: return os.path.exists(self.config_file_name) + async def get_template_file_str(self) -> str: + if self.template_resource_name is None: + return None + + with ( + resources.files('langbot.templates').joinpath(self.template_resource_name).open('r', encoding='utf-8') as f + ): + return f.read() + async def create(self): - if self.template_file_name is not None: - shutil.copyfile(self.template_file_name, self.config_file_name) + if await self.get_template_file_str() is not None: + with open(self.config_file_name, 'w', encoding='utf-8') as f: + f.write(await self.get_template_file_str()) elif self.template_data is not None: with open(self.config_file_name, 'w', encoding='utf-8') as f: yaml.dump(self.template_data, f, indent=4, allow_unicode=True) @@ -34,9 +44,10 @@ class YAMLConfigFile(file_model.ConfigFile): if not self.exists(): await self.create() - if self.template_file_name is not None: - with open(self.template_file_name, 'r', encoding='utf-8') as f: - self.template_data = yaml.load(f, Loader=yaml.FullLoader) + template_file_str = await self.get_template_file_str() + + if template_file_str is not None: + self.template_data = yaml.load(template_file_str, Loader=yaml.FullLoader) with open(self.config_file_name, 'r', encoding='utf-8') as f: try: diff --git a/pkg/config/manager.py b/src/langbot/pkg/config/manager.py similarity index 86% rename from pkg/config/manager.py rename to src/langbot/pkg/config/manager.py index d552b038..d22591b0 100644 --- a/pkg/config/manager.py +++ b/src/langbot/pkg/config/manager.py @@ -62,7 +62,7 @@ async def load_python_module_config(config_name: str, template_name: str, comple async def load_json_config( config_name: str, - template_name: str = None, + template_resource_name: str = None, template_data: dict = None, completion: bool = True, ) -> ConfigManager: @@ -70,11 +70,11 @@ async def load_json_config( Args: config_name (str): Config file name - template_name (str): Template file name + template_resource_name (str): Template resource name template_data (dict): Template data completion (bool): Whether to automatically complete the config file in memory """ - cfg_inst = json_file.JSONConfigFile(config_name, template_name, template_data) + cfg_inst = json_file.JSONConfigFile(config_name, template_resource_name, template_data) cfg_mgr = ConfigManager(cfg_inst) await cfg_mgr.load_config(completion=completion) @@ -84,7 +84,7 @@ async def load_json_config( async def load_yaml_config( config_name: str, - template_name: str = None, + template_resource_name: str = None, template_data: dict = None, completion: bool = True, ) -> ConfigManager: @@ -92,14 +92,14 @@ async def load_yaml_config( Args: config_name (str): Config file name - template_name (str): Template file name + template_resource_name (str): Template resource name template_data (dict): Template data completion (bool): Whether to automatically complete the config file in memory Returns: ConfigManager: Config file manager """ - cfg_inst = yaml_file.YAMLConfigFile(config_name, template_name, template_data) + cfg_inst = yaml_file.YAMLConfigFile(config_name, template_resource_name, template_data) cfg_mgr = ConfigManager(cfg_inst) await cfg_mgr.load_config(completion=completion) diff --git a/pkg/config/model.py b/src/langbot/pkg/config/model.py similarity index 100% rename from pkg/config/model.py rename to src/langbot/pkg/config/model.py diff --git a/pkg/core/__init__.py b/src/langbot/pkg/core/__init__.py similarity index 100% rename from pkg/core/__init__.py rename to src/langbot/pkg/core/__init__.py diff --git a/pkg/core/app.py b/src/langbot/pkg/core/app.py similarity index 96% rename from pkg/core/app.py rename to src/langbot/pkg/core/app.py index 88cd0d50..a3ad68e8 100644 --- a/pkg/core/app.py +++ b/src/langbot/pkg/core/app.py @@ -9,13 +9,13 @@ from ..platform import botmgr as im_mgr from ..platform.webhook_pusher import WebhookPusher from ..provider.session import sessionmgr as llm_session_mgr from ..provider.modelmgr import modelmgr as llm_model_mgr -from ..provider.tools import toolmgr as llm_tool_mgr +from langbot.pkg.provider.tools import toolmgr as llm_tool_mgr from ..config import manager as config_mgr from ..command import cmdmgr from ..plugin import connector as plugin_connector from ..pipeline import pool from ..pipeline import controller, pipelinemgr -from ..utils import version as version_mgr, proxy as proxy_mgr, announce as announce_mgr +from ..utils import version as version_mgr, proxy as proxy_mgr from ..persistence import mgr as persistencemgr from ..api.http.controller import main as http_controller from ..api.http.service import user as user_service @@ -95,8 +95,6 @@ class Application: ver_mgr: version_mgr.VersionManager = None - ann_mgr: announce_mgr.AnnouncementManager = None - proxy_mgr: proxy_mgr.ProxyManager = None logger: logging.Logger = None @@ -186,7 +184,11 @@ class Application: async def print_web_access_info(self): """Print access webui tips""" - if not os.path.exists(os.path.join('.', 'web/out')): + from ..utils import paths + + frontend_path = paths.get_frontend_path() + + if not os.path.exists(frontend_path): self.logger.warning('WebUI 文件缺失,请根据文档部署:https://docs.langbot.app/zh') self.logger.warning( 'WebUI files are missing, please deploy according to the documentation: https://docs.langbot.app/en' diff --git a/pkg/core/boot.py b/src/langbot/pkg/core/boot.py similarity index 100% rename from pkg/core/boot.py rename to src/langbot/pkg/core/boot.py diff --git a/pkg/core/bootutils/__init__.py b/src/langbot/pkg/core/bootutils/__init__.py similarity index 100% rename from pkg/core/bootutils/__init__.py rename to src/langbot/pkg/core/bootutils/__init__.py diff --git a/pkg/core/bootutils/config.py b/src/langbot/pkg/core/bootutils/config.py similarity index 100% rename from pkg/core/bootutils/config.py rename to src/langbot/pkg/core/bootutils/config.py diff --git a/pkg/core/bootutils/deps.py b/src/langbot/pkg/core/bootutils/deps.py similarity index 100% rename from pkg/core/bootutils/deps.py rename to src/langbot/pkg/core/bootutils/deps.py diff --git a/pkg/core/bootutils/files.py b/src/langbot/pkg/core/bootutils/files.py similarity index 77% rename from pkg/core/bootutils/files.py rename to src/langbot/pkg/core/bootutils/files.py index 07bb7779..067b60c1 100644 --- a/pkg/core/bootutils/files.py +++ b/src/langbot/pkg/core/bootutils/files.py @@ -20,6 +20,8 @@ required_paths = [ async def generate_files() -> list[str]: global required_files, required_paths + from ...utils import paths as path_utils + for required_paths in required_paths: if not os.path.exists(required_paths): os.mkdir(required_paths) @@ -27,7 +29,8 @@ async def generate_files() -> list[str]: generated_files = [] for file in required_files: if not os.path.exists(file): - shutil.copyfile(required_files[file], file) + template_path = path_utils.get_resource_path(required_files[file]) + shutil.copyfile(template_path, file) generated_files.append(file) return generated_files diff --git a/pkg/core/bootutils/log.py b/src/langbot/pkg/core/bootutils/log.py similarity index 100% rename from pkg/core/bootutils/log.py rename to src/langbot/pkg/core/bootutils/log.py diff --git a/pkg/core/entities.py b/src/langbot/pkg/core/entities.py similarity index 100% rename from pkg/core/entities.py rename to src/langbot/pkg/core/entities.py diff --git a/pkg/core/migration.py b/src/langbot/pkg/core/migration.py similarity index 100% rename from pkg/core/migration.py rename to src/langbot/pkg/core/migration.py diff --git a/pkg/core/migrations/__init__.py b/src/langbot/pkg/core/migrations/__init__.py similarity index 100% rename from pkg/core/migrations/__init__.py rename to src/langbot/pkg/core/migrations/__init__.py diff --git a/pkg/core/migrations/m001_sensitive_word_migration.py b/src/langbot/pkg/core/migrations/m001_sensitive_word_migration.py similarity index 100% rename from pkg/core/migrations/m001_sensitive_word_migration.py rename to src/langbot/pkg/core/migrations/m001_sensitive_word_migration.py diff --git a/pkg/core/migrations/m002_openai_config_migration.py b/src/langbot/pkg/core/migrations/m002_openai_config_migration.py similarity index 100% rename from pkg/core/migrations/m002_openai_config_migration.py rename to src/langbot/pkg/core/migrations/m002_openai_config_migration.py diff --git a/pkg/core/migrations/m003_anthropic_requester_cfg_completion.py b/src/langbot/pkg/core/migrations/m003_anthropic_requester_cfg_completion.py similarity index 100% rename from pkg/core/migrations/m003_anthropic_requester_cfg_completion.py rename to src/langbot/pkg/core/migrations/m003_anthropic_requester_cfg_completion.py diff --git a/pkg/core/migrations/m004_moonshot_cfg_completion.py b/src/langbot/pkg/core/migrations/m004_moonshot_cfg_completion.py similarity index 100% rename from pkg/core/migrations/m004_moonshot_cfg_completion.py rename to src/langbot/pkg/core/migrations/m004_moonshot_cfg_completion.py diff --git a/pkg/core/migrations/m005_deepseek_cfg_completion.py b/src/langbot/pkg/core/migrations/m005_deepseek_cfg_completion.py similarity index 100% rename from pkg/core/migrations/m005_deepseek_cfg_completion.py rename to src/langbot/pkg/core/migrations/m005_deepseek_cfg_completion.py diff --git a/pkg/core/migrations/m006_vision_config.py b/src/langbot/pkg/core/migrations/m006_vision_config.py similarity index 100% rename from pkg/core/migrations/m006_vision_config.py rename to src/langbot/pkg/core/migrations/m006_vision_config.py diff --git a/pkg/core/migrations/m007_qcg_center_url.py b/src/langbot/pkg/core/migrations/m007_qcg_center_url.py similarity index 100% rename from pkg/core/migrations/m007_qcg_center_url.py rename to src/langbot/pkg/core/migrations/m007_qcg_center_url.py diff --git a/pkg/core/migrations/m008_ad_fixwin_config_migrate.py b/src/langbot/pkg/core/migrations/m008_ad_fixwin_config_migrate.py similarity index 100% rename from pkg/core/migrations/m008_ad_fixwin_config_migrate.py rename to src/langbot/pkg/core/migrations/m008_ad_fixwin_config_migrate.py diff --git a/pkg/core/migrations/m009_msg_truncator_cfg.py b/src/langbot/pkg/core/migrations/m009_msg_truncator_cfg.py similarity index 100% rename from pkg/core/migrations/m009_msg_truncator_cfg.py rename to src/langbot/pkg/core/migrations/m009_msg_truncator_cfg.py diff --git a/pkg/core/migrations/m010_ollama_requester_config.py b/src/langbot/pkg/core/migrations/m010_ollama_requester_config.py similarity index 100% rename from pkg/core/migrations/m010_ollama_requester_config.py rename to src/langbot/pkg/core/migrations/m010_ollama_requester_config.py diff --git a/pkg/core/migrations/m011_command_prefix_config.py b/src/langbot/pkg/core/migrations/m011_command_prefix_config.py similarity index 100% rename from pkg/core/migrations/m011_command_prefix_config.py rename to src/langbot/pkg/core/migrations/m011_command_prefix_config.py diff --git a/pkg/core/migrations/m012_runner_config.py b/src/langbot/pkg/core/migrations/m012_runner_config.py similarity index 100% rename from pkg/core/migrations/m012_runner_config.py rename to src/langbot/pkg/core/migrations/m012_runner_config.py diff --git a/pkg/core/migrations/m013_http_api_config.py b/src/langbot/pkg/core/migrations/m013_http_api_config.py similarity index 100% rename from pkg/core/migrations/m013_http_api_config.py rename to src/langbot/pkg/core/migrations/m013_http_api_config.py diff --git a/pkg/core/migrations/m014_force_delay_config.py b/src/langbot/pkg/core/migrations/m014_force_delay_config.py similarity index 100% rename from pkg/core/migrations/m014_force_delay_config.py rename to src/langbot/pkg/core/migrations/m014_force_delay_config.py diff --git a/pkg/core/migrations/m015_gitee_ai_config.py b/src/langbot/pkg/core/migrations/m015_gitee_ai_config.py similarity index 100% rename from pkg/core/migrations/m015_gitee_ai_config.py rename to src/langbot/pkg/core/migrations/m015_gitee_ai_config.py diff --git a/pkg/core/migrations/m016_dify_service_api.py b/src/langbot/pkg/core/migrations/m016_dify_service_api.py similarity index 100% rename from pkg/core/migrations/m016_dify_service_api.py rename to src/langbot/pkg/core/migrations/m016_dify_service_api.py diff --git a/pkg/core/migrations/m017_dify_api_timeout_params.py b/src/langbot/pkg/core/migrations/m017_dify_api_timeout_params.py similarity index 100% rename from pkg/core/migrations/m017_dify_api_timeout_params.py rename to src/langbot/pkg/core/migrations/m017_dify_api_timeout_params.py diff --git a/pkg/core/migrations/m018_xai_config.py b/src/langbot/pkg/core/migrations/m018_xai_config.py similarity index 100% rename from pkg/core/migrations/m018_xai_config.py rename to src/langbot/pkg/core/migrations/m018_xai_config.py diff --git a/pkg/core/migrations/m019_zhipuai_config.py b/src/langbot/pkg/core/migrations/m019_zhipuai_config.py similarity index 100% rename from pkg/core/migrations/m019_zhipuai_config.py rename to src/langbot/pkg/core/migrations/m019_zhipuai_config.py diff --git a/pkg/core/migrations/m020_wecom_config.py b/src/langbot/pkg/core/migrations/m020_wecom_config.py similarity index 100% rename from pkg/core/migrations/m020_wecom_config.py rename to src/langbot/pkg/core/migrations/m020_wecom_config.py diff --git a/pkg/core/migrations/m021_lark_config.py b/src/langbot/pkg/core/migrations/m021_lark_config.py similarity index 100% rename from pkg/core/migrations/m021_lark_config.py rename to src/langbot/pkg/core/migrations/m021_lark_config.py diff --git a/pkg/core/migrations/m022_lmstudio_config.py b/src/langbot/pkg/core/migrations/m022_lmstudio_config.py similarity index 100% rename from pkg/core/migrations/m022_lmstudio_config.py rename to src/langbot/pkg/core/migrations/m022_lmstudio_config.py diff --git a/pkg/core/migrations/m023_siliconflow_config.py b/src/langbot/pkg/core/migrations/m023_siliconflow_config.py similarity index 100% rename from pkg/core/migrations/m023_siliconflow_config.py rename to src/langbot/pkg/core/migrations/m023_siliconflow_config.py diff --git a/pkg/core/migrations/m024_discord_config.py b/src/langbot/pkg/core/migrations/m024_discord_config.py similarity index 100% rename from pkg/core/migrations/m024_discord_config.py rename to src/langbot/pkg/core/migrations/m024_discord_config.py diff --git a/pkg/core/migrations/m025_gewechat_config.py b/src/langbot/pkg/core/migrations/m025_gewechat_config.py similarity index 100% rename from pkg/core/migrations/m025_gewechat_config.py rename to src/langbot/pkg/core/migrations/m025_gewechat_config.py diff --git a/pkg/core/migrations/m026_qqofficial_config.py b/src/langbot/pkg/core/migrations/m026_qqofficial_config.py similarity index 100% rename from pkg/core/migrations/m026_qqofficial_config.py rename to src/langbot/pkg/core/migrations/m026_qqofficial_config.py diff --git a/pkg/core/migrations/m027_wx_official_account_config.py b/src/langbot/pkg/core/migrations/m027_wx_official_account_config.py similarity index 100% rename from pkg/core/migrations/m027_wx_official_account_config.py rename to src/langbot/pkg/core/migrations/m027_wx_official_account_config.py diff --git a/pkg/core/migrations/m028_aliyun_requester_config.py b/src/langbot/pkg/core/migrations/m028_aliyun_requester_config.py similarity index 100% rename from pkg/core/migrations/m028_aliyun_requester_config.py rename to src/langbot/pkg/core/migrations/m028_aliyun_requester_config.py diff --git a/pkg/core/migrations/m029_dashscope_app_api_config.py b/src/langbot/pkg/core/migrations/m029_dashscope_app_api_config.py similarity index 100% rename from pkg/core/migrations/m029_dashscope_app_api_config.py rename to src/langbot/pkg/core/migrations/m029_dashscope_app_api_config.py diff --git a/pkg/core/migrations/m030_lark_config_cmpl.py b/src/langbot/pkg/core/migrations/m030_lark_config_cmpl.py similarity index 100% rename from pkg/core/migrations/m030_lark_config_cmpl.py rename to src/langbot/pkg/core/migrations/m030_lark_config_cmpl.py diff --git a/pkg/core/migrations/m031_dingtalk_config.py b/src/langbot/pkg/core/migrations/m031_dingtalk_config.py similarity index 100% rename from pkg/core/migrations/m031_dingtalk_config.py rename to src/langbot/pkg/core/migrations/m031_dingtalk_config.py diff --git a/pkg/core/migrations/m032_volcark_config.py b/src/langbot/pkg/core/migrations/m032_volcark_config.py similarity index 100% rename from pkg/core/migrations/m032_volcark_config.py rename to src/langbot/pkg/core/migrations/m032_volcark_config.py diff --git a/pkg/core/migrations/m033_dify_thinking_config.py b/src/langbot/pkg/core/migrations/m033_dify_thinking_config.py similarity index 100% rename from pkg/core/migrations/m033_dify_thinking_config.py rename to src/langbot/pkg/core/migrations/m033_dify_thinking_config.py diff --git a/pkg/core/migrations/m034_gewechat_file_url_config.py b/src/langbot/pkg/core/migrations/m034_gewechat_file_url_config.py similarity index 100% rename from pkg/core/migrations/m034_gewechat_file_url_config.py rename to src/langbot/pkg/core/migrations/m034_gewechat_file_url_config.py diff --git a/pkg/core/migrations/m035_wxoa_mode.py b/src/langbot/pkg/core/migrations/m035_wxoa_mode.py similarity index 100% rename from pkg/core/migrations/m035_wxoa_mode.py rename to src/langbot/pkg/core/migrations/m035_wxoa_mode.py diff --git a/pkg/core/migrations/m036_wxoa_loading_message.py b/src/langbot/pkg/core/migrations/m036_wxoa_loading_message.py similarity index 100% rename from pkg/core/migrations/m036_wxoa_loading_message.py rename to src/langbot/pkg/core/migrations/m036_wxoa_loading_message.py diff --git a/pkg/core/migrations/m037_mcp_config.py b/src/langbot/pkg/core/migrations/m037_mcp_config.py similarity index 100% rename from pkg/core/migrations/m037_mcp_config.py rename to src/langbot/pkg/core/migrations/m037_mcp_config.py diff --git a/pkg/core/migrations/m038_tg_dingtalk_markdown.py b/src/langbot/pkg/core/migrations/m038_tg_dingtalk_markdown.py similarity index 100% rename from pkg/core/migrations/m038_tg_dingtalk_markdown.py rename to src/langbot/pkg/core/migrations/m038_tg_dingtalk_markdown.py diff --git a/pkg/core/migrations/m039_modelscope_cfg_completion.py b/src/langbot/pkg/core/migrations/m039_modelscope_cfg_completion.py similarity index 100% rename from pkg/core/migrations/m039_modelscope_cfg_completion.py rename to src/langbot/pkg/core/migrations/m039_modelscope_cfg_completion.py diff --git a/pkg/core/migrations/m040_ppio_config.py b/src/langbot/pkg/core/migrations/m040_ppio_config.py similarity index 100% rename from pkg/core/migrations/m040_ppio_config.py rename to src/langbot/pkg/core/migrations/m040_ppio_config.py diff --git a/pkg/core/note.py b/src/langbot/pkg/core/note.py similarity index 100% rename from pkg/core/note.py rename to src/langbot/pkg/core/note.py diff --git a/pkg/core/notes/__init__.py b/src/langbot/pkg/core/notes/__init__.py similarity index 100% rename from pkg/core/notes/__init__.py rename to src/langbot/pkg/core/notes/__init__.py diff --git a/pkg/core/notes/n001_classic_msgs.py b/src/langbot/pkg/core/notes/n001_classic_msgs.py similarity index 87% rename from pkg/core/notes/n001_classic_msgs.py rename to src/langbot/pkg/core/notes/n001_classic_msgs.py index 265ddbe9..190958c6 100644 --- a/pkg/core/notes/n001_classic_msgs.py +++ b/src/langbot/pkg/core/notes/n001_classic_msgs.py @@ -13,6 +13,4 @@ class ClassicNotes(note.LaunchNote): return True async def yield_note(self) -> typing.AsyncGenerator[typing.Tuple[str, int], None]: - yield await self.ap.ann_mgr.show_announcements() - yield await self.ap.ver_mgr.show_version_update() diff --git a/pkg/core/notes/n002_selection_mode_on_windows.py b/src/langbot/pkg/core/notes/n002_selection_mode_on_windows.py similarity index 100% rename from pkg/core/notes/n002_selection_mode_on_windows.py rename to src/langbot/pkg/core/notes/n002_selection_mode_on_windows.py diff --git a/pkg/core/notes/n003_print_version.py b/src/langbot/pkg/core/notes/n003_print_version.py similarity index 100% rename from pkg/core/notes/n003_print_version.py rename to src/langbot/pkg/core/notes/n003_print_version.py diff --git a/pkg/core/stage.py b/src/langbot/pkg/core/stage.py similarity index 100% rename from pkg/core/stage.py rename to src/langbot/pkg/core/stage.py diff --git a/pkg/core/stages/__init__.py b/src/langbot/pkg/core/stages/__init__.py similarity index 100% rename from pkg/core/stages/__init__.py rename to src/langbot/pkg/core/stages/__init__.py diff --git a/pkg/core/stages/build_app.py b/src/langbot/pkg/core/stages/build_app.py similarity index 96% rename from pkg/core/stages/build_app.py rename to src/langbot/pkg/core/stages/build_app.py index 407b929d..51fd9a9f 100644 --- a/pkg/core/stages/build_app.py +++ b/src/langbot/pkg/core/stages/build_app.py @@ -3,7 +3,7 @@ from __future__ import annotations import asyncio from .. import stage, app -from ...utils import version, proxy, announce +from ...utils import version, proxy from ...pipeline import pool, controller, pipelinemgr from ...plugin import connector as plugin_connector from ...command import cmdmgr @@ -39,7 +39,7 @@ class BuildAppStage(stage.BootingStage): ap.task_mgr = taskmgr.AsyncTaskManager(ap) discover = discover_engine.ComponentDiscoveryEngine(ap) - discover.discover_blueprint('components.yaml') + discover.discover_blueprint('templates/components.yaml') ap.discover = discover proxy_mgr = proxy.ProxyManager(ap) @@ -50,10 +50,6 @@ class BuildAppStage(stage.BootingStage): await ver_mgr.initialize() ap.ver_mgr = ver_mgr - # Send announcement - ann_mgr = announce.AnnouncementManager(ap) - ap.ann_mgr = ann_mgr - ap.query_pool = pool.QueryPool() log_cache = logcache.LogCache() diff --git a/pkg/core/stages/genkeys.py b/src/langbot/pkg/core/stages/genkeys.py similarity index 100% rename from pkg/core/stages/genkeys.py rename to src/langbot/pkg/core/stages/genkeys.py diff --git a/pkg/core/stages/load_config.py b/src/langbot/pkg/core/stages/load_config.py similarity index 60% rename from pkg/core/stages/load_config.py rename to src/langbot/pkg/core/stages/load_config.py index 2ef5623e..b2b5abba 100644 --- a/pkg/core/stages/load_config.py +++ b/src/langbot/pkg/core/stages/load_config.py @@ -2,6 +2,8 @@ from __future__ import annotations import os from typing import Any +import yaml +import importlib.resources as resources from .. import stage, app from ..bootutils import config @@ -95,47 +97,45 @@ class LoadConfigStage(stage.BootingStage): async def run(self, ap: app.Application): """Load config file""" - # ======= deprecated ======= - if os.path.exists('data/config/command.json'): - ap.command_cfg = await config.load_json_config( - 'data/config/command.json', - 'templates/legacy/command.json', - completion=False, - ) + # # ======= deprecated ======= + # if os.path.exists('data/config/command.json'): + # ap.command_cfg = await config.load_json_config( + # 'data/config/command.json', + # 'templates/legacy/command.json', + # completion=False, + # ) - if os.path.exists('data/config/pipeline.json'): - ap.pipeline_cfg = await config.load_json_config( - 'data/config/pipeline.json', - 'templates/legacy/pipeline.json', - completion=False, - ) + # if os.path.exists('data/config/pipeline.json'): + # ap.pipeline_cfg = await config.load_json_config( + # 'data/config/pipeline.json', + # 'templates/legacy/pipeline.json', + # completion=False, + # ) - if os.path.exists('data/config/platform.json'): - ap.platform_cfg = await config.load_json_config( - 'data/config/platform.json', - 'templates/legacy/platform.json', - completion=False, - ) + # if os.path.exists('data/config/platform.json'): + # ap.platform_cfg = await config.load_json_config( + # 'data/config/platform.json', + # 'templates/legacy/platform.json', + # completion=False, + # ) - if os.path.exists('data/config/provider.json'): - ap.provider_cfg = await config.load_json_config( - 'data/config/provider.json', - 'templates/legacy/provider.json', - completion=False, - ) + # if os.path.exists('data/config/provider.json'): + # ap.provider_cfg = await config.load_json_config( + # 'data/config/provider.json', + # 'templates/legacy/provider.json', + # completion=False, + # ) - if os.path.exists('data/config/system.json'): - ap.system_cfg = await config.load_json_config( - 'data/config/system.json', - 'templates/legacy/system.json', - completion=False, - ) + # if os.path.exists('data/config/system.json'): + # ap.system_cfg = await config.load_json_config( + # 'data/config/system.json', + # 'templates/legacy/system.json', + # completion=False, + # ) - # ======= deprecated ======= + # # ======= deprecated ======= - ap.instance_config = await config.load_yaml_config( - 'data/config.yaml', 'templates/config.yaml', completion=False - ) + ap.instance_config = await config.load_yaml_config('data/config.yaml', 'config.yaml', completion=False) # Apply environment variable overrides to data/config.yaml ap.instance_config.data = _apply_env_overrides_to_config(ap.instance_config.data) @@ -144,22 +144,15 @@ class LoadConfigStage(stage.BootingStage): ap.sensitive_meta = await config.load_json_config( 'data/metadata/sensitive-words.json', - 'templates/metadata/sensitive-words.json', + 'metadata/sensitive-words.json', ) await ap.sensitive_meta.dump_config() - ap.pipeline_config_meta_trigger = await config.load_yaml_config( - 'templates/metadata/pipeline/trigger.yaml', - 'templates/metadata/pipeline/trigger.yaml', - ) - ap.pipeline_config_meta_safety = await config.load_yaml_config( - 'templates/metadata/pipeline/safety.yaml', - 'templates/metadata/pipeline/safety.yaml', - ) - ap.pipeline_config_meta_ai = await config.load_yaml_config( - 'templates/metadata/pipeline/ai.yaml', 'templates/metadata/pipeline/ai.yaml' - ) - ap.pipeline_config_meta_output = await config.load_yaml_config( - 'templates/metadata/pipeline/output.yaml', - 'templates/metadata/pipeline/output.yaml', - ) + async def load_resource_yaml_template_data(resource_name: str) -> dict: + with resources.files('langbot.templates').joinpath(resource_name).open('r', encoding='utf-8') as f: + return yaml.load(f, Loader=yaml.FullLoader) + + ap.pipeline_config_meta_trigger = await load_resource_yaml_template_data('metadata/pipeline/trigger.yaml') + ap.pipeline_config_meta_safety = await load_resource_yaml_template_data('metadata/pipeline/safety.yaml') + ap.pipeline_config_meta_ai = await load_resource_yaml_template_data('metadata/pipeline/ai.yaml') + ap.pipeline_config_meta_output = await load_resource_yaml_template_data('metadata/pipeline/output.yaml') diff --git a/pkg/core/stages/migrate.py b/src/langbot/pkg/core/stages/migrate.py similarity index 100% rename from pkg/core/stages/migrate.py rename to src/langbot/pkg/core/stages/migrate.py diff --git a/pkg/core/stages/setup_logger.py b/src/langbot/pkg/core/stages/setup_logger.py similarity index 100% rename from pkg/core/stages/setup_logger.py rename to src/langbot/pkg/core/stages/setup_logger.py diff --git a/pkg/core/stages/show_notes.py b/src/langbot/pkg/core/stages/show_notes.py similarity index 100% rename from pkg/core/stages/show_notes.py rename to src/langbot/pkg/core/stages/show_notes.py diff --git a/pkg/core/taskmgr.py b/src/langbot/pkg/core/taskmgr.py similarity index 100% rename from pkg/core/taskmgr.py rename to src/langbot/pkg/core/taskmgr.py diff --git a/pkg/discover/__init__.py b/src/langbot/pkg/discover/__init__.py similarity index 100% rename from pkg/discover/__init__.py rename to src/langbot/pkg/discover/__init__.py diff --git a/pkg/discover/engine.py b/src/langbot/pkg/discover/engine.py similarity index 91% rename from pkg/discover/engine.py rename to src/langbot/pkg/discover/engine.py index 335862c0..ca7f2588 100644 --- a/pkg/discover/engine.py +++ b/src/langbot/pkg/discover/engine.py @@ -6,7 +6,8 @@ import os import yaml import pydantic -from ..core import app +from langbot.pkg.core import app +from langbot.pkg.utils import importutil class I18nString(pydantic.BaseModel): @@ -165,7 +166,7 @@ class Component(pydantic.BaseModel): if module_path.endswith('.py'): module_path = module_path[:-3] module_path = module_path.replace('/', '.').replace('\\', '.') - module = importlib.import_module(module_path) + module = importlib.import_module(f'langbot.{module_path}') return getattr(module, self.execution.python.attr) def to_plain_dict(self) -> dict: @@ -193,16 +194,17 @@ class ComponentDiscoveryEngine: def load_component_manifest(self, path: str, owner: str = 'builtin', no_save: bool = False) -> Component | None: """加载组件清单""" - with open(path, 'r', encoding='utf-8') as f: - manifest = yaml.safe_load(f) - if not Component.is_component_manifest(manifest): - return None - comp = Component(owner=owner, manifest=manifest, rel_path=path) - if not no_save: - if comp.kind not in self.components: - self.components[comp.kind] = [] - self.components[comp.kind].append(comp) - return comp + # with open(path, 'r', encoding='utf-8') as f: + # manifest = yaml.safe_load(f) + manifest = yaml.safe_load(importutil.read_resource_file(path)) + if not Component.is_component_manifest(manifest): + return None + comp = Component(owner=owner, manifest=manifest, rel_path=path) + if not no_save: + if comp.kind not in self.components: + self.components[comp.kind] = [] + self.components[comp.kind].append(comp) + return comp def load_component_manifests_in_dir( self, @@ -217,7 +219,8 @@ class ComponentDiscoveryEngine: def recursive_load_component_manifests_in_dir(path: str, depth: int = 1): if depth > max_depth: return - for file in os.listdir(path): + + for file in importutil.list_resource_files(path): if (not os.path.isdir(os.path.join(path, file))) and (file.endswith('.yaml') or file.endswith('.yml')): comp = self.load_component_manifest(os.path.join(path, file), owner, no_save) if comp is not None: diff --git a/pkg/entity/__init__.py b/src/langbot/pkg/entity/__init__.py similarity index 100% rename from pkg/entity/__init__.py rename to src/langbot/pkg/entity/__init__.py diff --git a/pkg/entity/errors/__init__.py b/src/langbot/pkg/entity/errors/__init__.py similarity index 100% rename from pkg/entity/errors/__init__.py rename to src/langbot/pkg/entity/errors/__init__.py diff --git a/pkg/entity/errors/platform.py b/src/langbot/pkg/entity/errors/platform.py similarity index 100% rename from pkg/entity/errors/platform.py rename to src/langbot/pkg/entity/errors/platform.py diff --git a/pkg/entity/errors/provider.py b/src/langbot/pkg/entity/errors/provider.py similarity index 100% rename from pkg/entity/errors/provider.py rename to src/langbot/pkg/entity/errors/provider.py diff --git a/pkg/entity/persistence/__init__.py b/src/langbot/pkg/entity/persistence/__init__.py similarity index 100% rename from pkg/entity/persistence/__init__.py rename to src/langbot/pkg/entity/persistence/__init__.py diff --git a/pkg/entity/persistence/apikey.py b/src/langbot/pkg/entity/persistence/apikey.py similarity index 100% rename from pkg/entity/persistence/apikey.py rename to src/langbot/pkg/entity/persistence/apikey.py diff --git a/pkg/entity/persistence/base.py b/src/langbot/pkg/entity/persistence/base.py similarity index 100% rename from pkg/entity/persistence/base.py rename to src/langbot/pkg/entity/persistence/base.py diff --git a/pkg/entity/persistence/bot.py b/src/langbot/pkg/entity/persistence/bot.py similarity index 100% rename from pkg/entity/persistence/bot.py rename to src/langbot/pkg/entity/persistence/bot.py diff --git a/pkg/entity/persistence/bstorage.py b/src/langbot/pkg/entity/persistence/bstorage.py similarity index 100% rename from pkg/entity/persistence/bstorage.py rename to src/langbot/pkg/entity/persistence/bstorage.py diff --git a/pkg/entity/persistence/mcp.py b/src/langbot/pkg/entity/persistence/mcp.py similarity index 100% rename from pkg/entity/persistence/mcp.py rename to src/langbot/pkg/entity/persistence/mcp.py diff --git a/pkg/entity/persistence/metadata.py b/src/langbot/pkg/entity/persistence/metadata.py similarity index 100% rename from pkg/entity/persistence/metadata.py rename to src/langbot/pkg/entity/persistence/metadata.py diff --git a/pkg/entity/persistence/model.py b/src/langbot/pkg/entity/persistence/model.py similarity index 100% rename from pkg/entity/persistence/model.py rename to src/langbot/pkg/entity/persistence/model.py diff --git a/pkg/entity/persistence/pipeline.py b/src/langbot/pkg/entity/persistence/pipeline.py similarity index 100% rename from pkg/entity/persistence/pipeline.py rename to src/langbot/pkg/entity/persistence/pipeline.py diff --git a/pkg/entity/persistence/plugin.py b/src/langbot/pkg/entity/persistence/plugin.py similarity index 100% rename from pkg/entity/persistence/plugin.py rename to src/langbot/pkg/entity/persistence/plugin.py diff --git a/pkg/entity/persistence/rag.py b/src/langbot/pkg/entity/persistence/rag.py similarity index 100% rename from pkg/entity/persistence/rag.py rename to src/langbot/pkg/entity/persistence/rag.py diff --git a/pkg/entity/persistence/user.py b/src/langbot/pkg/entity/persistence/user.py similarity index 100% rename from pkg/entity/persistence/user.py rename to src/langbot/pkg/entity/persistence/user.py diff --git a/pkg/entity/persistence/vector.py b/src/langbot/pkg/entity/persistence/vector.py similarity index 100% rename from pkg/entity/persistence/vector.py rename to src/langbot/pkg/entity/persistence/vector.py diff --git a/pkg/entity/persistence/webhook.py b/src/langbot/pkg/entity/persistence/webhook.py similarity index 100% rename from pkg/entity/persistence/webhook.py rename to src/langbot/pkg/entity/persistence/webhook.py diff --git a/pkg/entity/rag/__init__.py b/src/langbot/pkg/entity/rag/__init__.py similarity index 100% rename from pkg/entity/rag/__init__.py rename to src/langbot/pkg/entity/rag/__init__.py diff --git a/pkg/entity/rag/retriever.py b/src/langbot/pkg/entity/rag/retriever.py similarity index 100% rename from pkg/entity/rag/retriever.py rename to src/langbot/pkg/entity/rag/retriever.py diff --git a/pkg/persistence/__init__.py b/src/langbot/pkg/persistence/__init__.py similarity index 100% rename from pkg/persistence/__init__.py rename to src/langbot/pkg/persistence/__init__.py diff --git a/pkg/persistence/database.py b/src/langbot/pkg/persistence/database.py similarity index 100% rename from pkg/persistence/database.py rename to src/langbot/pkg/persistence/database.py diff --git a/pkg/persistence/databases/__init__.py b/src/langbot/pkg/persistence/databases/__init__.py similarity index 100% rename from pkg/persistence/databases/__init__.py rename to src/langbot/pkg/persistence/databases/__init__.py diff --git a/pkg/persistence/databases/postgresql.py b/src/langbot/pkg/persistence/databases/postgresql.py similarity index 100% rename from pkg/persistence/databases/postgresql.py rename to src/langbot/pkg/persistence/databases/postgresql.py diff --git a/pkg/persistence/databases/sqlite.py b/src/langbot/pkg/persistence/databases/sqlite.py similarity index 100% rename from pkg/persistence/databases/sqlite.py rename to src/langbot/pkg/persistence/databases/sqlite.py diff --git a/pkg/persistence/mgr.py b/src/langbot/pkg/persistence/mgr.py similarity index 97% rename from pkg/persistence/mgr.py rename to src/langbot/pkg/persistence/mgr.py index 8390a506..53b20011 100644 --- a/pkg/persistence/mgr.py +++ b/src/langbot/pkg/persistence/mgr.py @@ -107,7 +107,7 @@ class PersistenceManager: if result.first() is None: self.ap.logger.info('Creating default pipeline...') - pipeline_config = json.load(open('templates/default-pipeline-config.json', 'r', encoding='utf-8')) + pipeline_config = json.loads(importutil.read_resource_file('templates/default-pipeline-config.json')) default_pipeline_uuid = str(uuid.uuid4()) pipeline_data = { diff --git a/pkg/persistence/migration.py b/src/langbot/pkg/persistence/migration.py similarity index 100% rename from pkg/persistence/migration.py rename to src/langbot/pkg/persistence/migration.py diff --git a/pkg/persistence/migrations/__init__.py b/src/langbot/pkg/persistence/migrations/__init__.py similarity index 100% rename from pkg/persistence/migrations/__init__.py rename to src/langbot/pkg/persistence/migrations/__init__.py diff --git a/pkg/persistence/migrations/dbm001_migrate_v3_config.py b/src/langbot/pkg/persistence/migrations/dbm001_migrate_v3_config.py similarity index 99% rename from pkg/persistence/migrations/dbm001_migrate_v3_config.py rename to src/langbot/pkg/persistence/migrations/dbm001_migrate_v3_config.py index 1f2d9770..55e63fff 100644 --- a/pkg/persistence/migrations/dbm001_migrate_v3_config.py +++ b/src/langbot/pkg/persistence/migrations/dbm001_migrate_v3_config.py @@ -212,7 +212,9 @@ class DBMigrateV3Config(migration.DBMigration): self.ap.instance_config.data['api']['port'] = self.ap.system_cfg.data['http-api']['port'] self.ap.instance_config.data['command'] = { 'prefix': self.ap.command_cfg.data['command-prefix'], - 'enable': self.ap.command_cfg.data['command-enable'] if 'command-enable' in self.ap.command_cfg.data else True, + 'enable': self.ap.command_cfg.data['command-enable'] + if 'command-enable' in self.ap.command_cfg.data + else True, 'privilege': self.ap.command_cfg.data['privilege'], } self.ap.instance_config.data['concurrency']['pipeline'] = self.ap.system_cfg.data['pipeline-concurrency'] diff --git a/pkg/persistence/migrations/dbm002_combine_quote_msg_config.py b/src/langbot/pkg/persistence/migrations/dbm002_combine_quote_msg_config.py similarity index 100% rename from pkg/persistence/migrations/dbm002_combine_quote_msg_config.py rename to src/langbot/pkg/persistence/migrations/dbm002_combine_quote_msg_config.py diff --git a/pkg/persistence/migrations/dbm003_n8n_config.py b/src/langbot/pkg/persistence/migrations/dbm003_n8n_config.py similarity index 100% rename from pkg/persistence/migrations/dbm003_n8n_config.py rename to src/langbot/pkg/persistence/migrations/dbm003_n8n_config.py diff --git a/pkg/persistence/migrations/dbm004_rag_kb_uuid.py b/src/langbot/pkg/persistence/migrations/dbm004_rag_kb_uuid.py similarity index 100% rename from pkg/persistence/migrations/dbm004_rag_kb_uuid.py rename to src/langbot/pkg/persistence/migrations/dbm004_rag_kb_uuid.py diff --git a/pkg/persistence/migrations/dbm005_pipeline_remove_cot_config.py b/src/langbot/pkg/persistence/migrations/dbm005_pipeline_remove_cot_config.py similarity index 100% rename from pkg/persistence/migrations/dbm005_pipeline_remove_cot_config.py rename to src/langbot/pkg/persistence/migrations/dbm005_pipeline_remove_cot_config.py diff --git a/pkg/persistence/migrations/dbm006_langflow_api_config.py b/src/langbot/pkg/persistence/migrations/dbm006_langflow_api_config.py similarity index 100% rename from pkg/persistence/migrations/dbm006_langflow_api_config.py rename to src/langbot/pkg/persistence/migrations/dbm006_langflow_api_config.py diff --git a/pkg/persistence/migrations/dbm007_plugin_install_source.py b/src/langbot/pkg/persistence/migrations/dbm007_plugin_install_source.py similarity index 100% rename from pkg/persistence/migrations/dbm007_plugin_install_source.py rename to src/langbot/pkg/persistence/migrations/dbm007_plugin_install_source.py diff --git a/pkg/persistence/migrations/dbm008_plugin_config.py b/src/langbot/pkg/persistence/migrations/dbm008_plugin_config.py similarity index 100% rename from pkg/persistence/migrations/dbm008_plugin_config.py rename to src/langbot/pkg/persistence/migrations/dbm008_plugin_config.py diff --git a/pkg/persistence/migrations/dbm009_pipeline_extension_preferences.py b/src/langbot/pkg/persistence/migrations/dbm009_pipeline_extension_preferences.py similarity index 100% rename from pkg/persistence/migrations/dbm009_pipeline_extension_preferences.py rename to src/langbot/pkg/persistence/migrations/dbm009_pipeline_extension_preferences.py diff --git a/pkg/persistence/migrations/dbm010_pipeline_multi_knowledge_base.py b/src/langbot/pkg/persistence/migrations/dbm010_pipeline_multi_knowledge_base.py similarity index 97% rename from pkg/persistence/migrations/dbm010_pipeline_multi_knowledge_base.py rename to src/langbot/pkg/persistence/migrations/dbm010_pipeline_multi_knowledge_base.py index c28b64ed..cb95c6a5 100644 --- a/pkg/persistence/migrations/dbm010_pipeline_multi_knowledge_base.py +++ b/src/langbot/pkg/persistence/migrations/dbm010_pipeline_multi_knowledge_base.py @@ -22,17 +22,17 @@ class DBMigratePipelineMultiKnowledgeBase(migration.DBMigration): # Convert knowledge-base from string to array if 'local-agent' in config['ai']: current_kb = config['ai']['local-agent'].get('knowledge-base', '') - + # If it's already a list, skip if isinstance(current_kb, list): continue - + # Convert string to list if current_kb and current_kb != '__none__': config['ai']['local-agent']['knowledge-bases'] = [current_kb] else: config['ai']['local-agent']['knowledge-bases'] = [] - + # Remove old field if 'knowledge-base' in config['ai']['local-agent']: del config['ai']['local-agent']['knowledge-base'] @@ -61,17 +61,17 @@ class DBMigratePipelineMultiKnowledgeBase(migration.DBMigration): # Convert knowledge-bases from array back to string if 'local-agent' in config['ai']: current_kbs = config['ai']['local-agent'].get('knowledge-bases', []) - + # If it's already a string, skip if isinstance(current_kbs, str): continue - + # Convert list to string (take first one or empty) if current_kbs and len(current_kbs) > 0: config['ai']['local-agent']['knowledge-base'] = current_kbs[0] else: config['ai']['local-agent']['knowledge-base'] = '' - + # Remove new field if 'knowledge-bases' in config['ai']['local-agent']: del config['ai']['local-agent']['knowledge-bases'] diff --git a/pkg/persistence/migrations/dbm011_dify_base_prompt_config.py b/src/langbot/pkg/persistence/migrations/dbm011_dify_base_prompt_config.py similarity index 100% rename from pkg/persistence/migrations/dbm011_dify_base_prompt_config.py rename to src/langbot/pkg/persistence/migrations/dbm011_dify_base_prompt_config.py diff --git a/pkg/pipeline/__init__.py b/src/langbot/pkg/pipeline/__init__.py similarity index 100% rename from pkg/pipeline/__init__.py rename to src/langbot/pkg/pipeline/__init__.py diff --git a/pkg/pipeline/bansess/__init__.py b/src/langbot/pkg/pipeline/bansess/__init__.py similarity index 100% rename from pkg/pipeline/bansess/__init__.py rename to src/langbot/pkg/pipeline/bansess/__init__.py diff --git a/pkg/pipeline/bansess/bansess.py b/src/langbot/pkg/pipeline/bansess/bansess.py similarity index 100% rename from pkg/pipeline/bansess/bansess.py rename to src/langbot/pkg/pipeline/bansess/bansess.py diff --git a/pkg/pipeline/cntfilter/__init__.py b/src/langbot/pkg/pipeline/cntfilter/__init__.py similarity index 100% rename from pkg/pipeline/cntfilter/__init__.py rename to src/langbot/pkg/pipeline/cntfilter/__init__.py diff --git a/pkg/pipeline/cntfilter/cntfilter.py b/src/langbot/pkg/pipeline/cntfilter/cntfilter.py similarity index 100% rename from pkg/pipeline/cntfilter/cntfilter.py rename to src/langbot/pkg/pipeline/cntfilter/cntfilter.py diff --git a/pkg/pipeline/cntfilter/entities.py b/src/langbot/pkg/pipeline/cntfilter/entities.py similarity index 100% rename from pkg/pipeline/cntfilter/entities.py rename to src/langbot/pkg/pipeline/cntfilter/entities.py diff --git a/pkg/pipeline/cntfilter/filter.py b/src/langbot/pkg/pipeline/cntfilter/filter.py similarity index 100% rename from pkg/pipeline/cntfilter/filter.py rename to src/langbot/pkg/pipeline/cntfilter/filter.py diff --git a/pkg/pipeline/cntfilter/filters/__init__.py b/src/langbot/pkg/pipeline/cntfilter/filters/__init__.py similarity index 100% rename from pkg/pipeline/cntfilter/filters/__init__.py rename to src/langbot/pkg/pipeline/cntfilter/filters/__init__.py diff --git a/pkg/pipeline/cntfilter/filters/baiduexamine.py b/src/langbot/pkg/pipeline/cntfilter/filters/baiduexamine.py similarity index 100% rename from pkg/pipeline/cntfilter/filters/baiduexamine.py rename to src/langbot/pkg/pipeline/cntfilter/filters/baiduexamine.py diff --git a/pkg/pipeline/cntfilter/filters/banwords.py b/src/langbot/pkg/pipeline/cntfilter/filters/banwords.py similarity index 100% rename from pkg/pipeline/cntfilter/filters/banwords.py rename to src/langbot/pkg/pipeline/cntfilter/filters/banwords.py diff --git a/pkg/pipeline/cntfilter/filters/cntignore.py b/src/langbot/pkg/pipeline/cntfilter/filters/cntignore.py similarity index 100% rename from pkg/pipeline/cntfilter/filters/cntignore.py rename to src/langbot/pkg/pipeline/cntfilter/filters/cntignore.py diff --git a/pkg/pipeline/controller.py b/src/langbot/pkg/pipeline/controller.py similarity index 100% rename from pkg/pipeline/controller.py rename to src/langbot/pkg/pipeline/controller.py diff --git a/pkg/pipeline/entities.py b/src/langbot/pkg/pipeline/entities.py similarity index 100% rename from pkg/pipeline/entities.py rename to src/langbot/pkg/pipeline/entities.py diff --git a/pkg/pipeline/longtext/__init__.py b/src/langbot/pkg/pipeline/longtext/__init__.py similarity index 100% rename from pkg/pipeline/longtext/__init__.py rename to src/langbot/pkg/pipeline/longtext/__init__.py diff --git a/pkg/pipeline/longtext/longtext.py b/src/langbot/pkg/pipeline/longtext/longtext.py similarity index 100% rename from pkg/pipeline/longtext/longtext.py rename to src/langbot/pkg/pipeline/longtext/longtext.py diff --git a/pkg/pipeline/longtext/strategies/__init__.py b/src/langbot/pkg/pipeline/longtext/strategies/__init__.py similarity index 100% rename from pkg/pipeline/longtext/strategies/__init__.py rename to src/langbot/pkg/pipeline/longtext/strategies/__init__.py diff --git a/pkg/pipeline/longtext/strategies/forward.py b/src/langbot/pkg/pipeline/longtext/strategies/forward.py similarity index 100% rename from pkg/pipeline/longtext/strategies/forward.py rename to src/langbot/pkg/pipeline/longtext/strategies/forward.py diff --git a/pkg/pipeline/longtext/strategies/image.py b/src/langbot/pkg/pipeline/longtext/strategies/image.py similarity index 100% rename from pkg/pipeline/longtext/strategies/image.py rename to src/langbot/pkg/pipeline/longtext/strategies/image.py diff --git a/pkg/pipeline/longtext/strategy.py b/src/langbot/pkg/pipeline/longtext/strategy.py similarity index 100% rename from pkg/pipeline/longtext/strategy.py rename to src/langbot/pkg/pipeline/longtext/strategy.py diff --git a/pkg/pipeline/msgtrun/__init__.py b/src/langbot/pkg/pipeline/msgtrun/__init__.py similarity index 100% rename from pkg/pipeline/msgtrun/__init__.py rename to src/langbot/pkg/pipeline/msgtrun/__init__.py diff --git a/pkg/pipeline/msgtrun/msgtrun.py b/src/langbot/pkg/pipeline/msgtrun/msgtrun.py similarity index 100% rename from pkg/pipeline/msgtrun/msgtrun.py rename to src/langbot/pkg/pipeline/msgtrun/msgtrun.py diff --git a/pkg/pipeline/msgtrun/truncator.py b/src/langbot/pkg/pipeline/msgtrun/truncator.py similarity index 100% rename from pkg/pipeline/msgtrun/truncator.py rename to src/langbot/pkg/pipeline/msgtrun/truncator.py diff --git a/pkg/pipeline/msgtrun/truncators/__init__.py b/src/langbot/pkg/pipeline/msgtrun/truncators/__init__.py similarity index 100% rename from pkg/pipeline/msgtrun/truncators/__init__.py rename to src/langbot/pkg/pipeline/msgtrun/truncators/__init__.py diff --git a/pkg/pipeline/msgtrun/truncators/round.py b/src/langbot/pkg/pipeline/msgtrun/truncators/round.py similarity index 100% rename from pkg/pipeline/msgtrun/truncators/round.py rename to src/langbot/pkg/pipeline/msgtrun/truncators/round.py diff --git a/pkg/pipeline/pipelinemgr.py b/src/langbot/pkg/pipeline/pipelinemgr.py similarity index 99% rename from pkg/pipeline/pipelinemgr.py rename to src/langbot/pkg/pipeline/pipelinemgr.py index c4206c0d..9470eb23 100644 --- a/pkg/pipeline/pipelinemgr.py +++ b/src/langbot/pkg/pipeline/pipelinemgr.py @@ -68,10 +68,10 @@ class RuntimePipeline: stage_containers: list[StageInstContainer] """阶段实例容器""" - + bound_plugins: list[str] """绑定到此流水线的插件列表(格式:author/plugin_name)""" - + bound_mcp_servers: list[str] """绑定到此流水线的MCP服务器列表(格式:uuid)""" @@ -84,12 +84,12 @@ class RuntimePipeline: self.ap = ap self.pipeline_entity = pipeline_entity self.stage_containers = stage_containers - + # Extract bound plugins and MCP servers from extensions_preferences extensions_prefs = pipeline_entity.extensions_preferences or {} plugin_list = extensions_prefs.get('plugins', []) - self.bound_plugins = [f"{p['author']}/{p['name']}" for p in plugin_list] if plugin_list else [] - + self.bound_plugins = [f'{p["author"]}/{p["name"]}' for p in plugin_list] if plugin_list else [] + mcp_server_list = extensions_prefs.get('mcp_servers', []) self.bound_mcp_servers = mcp_server_list if mcp_server_list else [] @@ -207,7 +207,7 @@ class RuntimePipeline: try: # Get bound plugins for this pipeline bound_plugins = query.variables.get('_pipeline_bound_plugins', None) - + # ======== 触发 MessageReceived 事件 ======== event_type = ( events.PersonMessageReceived diff --git a/pkg/pipeline/pool.py b/src/langbot/pkg/pipeline/pool.py similarity index 100% rename from pkg/pipeline/pool.py rename to src/langbot/pkg/pipeline/pool.py diff --git a/pkg/pipeline/preproc/__init__.py b/src/langbot/pkg/pipeline/preproc/__init__.py similarity index 100% rename from pkg/pipeline/preproc/__init__.py rename to src/langbot/pkg/pipeline/preproc/__init__.py diff --git a/pkg/pipeline/preproc/preproc.py b/src/langbot/pkg/pipeline/preproc/preproc.py similarity index 100% rename from pkg/pipeline/preproc/preproc.py rename to src/langbot/pkg/pipeline/preproc/preproc.py diff --git a/pkg/pipeline/process/__init__.py b/src/langbot/pkg/pipeline/process/__init__.py similarity index 100% rename from pkg/pipeline/process/__init__.py rename to src/langbot/pkg/pipeline/process/__init__.py diff --git a/pkg/pipeline/process/handler.py b/src/langbot/pkg/pipeline/process/handler.py similarity index 100% rename from pkg/pipeline/process/handler.py rename to src/langbot/pkg/pipeline/process/handler.py diff --git a/pkg/pipeline/process/handlers/__init__.py b/src/langbot/pkg/pipeline/process/handlers/__init__.py similarity index 100% rename from pkg/pipeline/process/handlers/__init__.py rename to src/langbot/pkg/pipeline/process/handlers/__init__.py diff --git a/pkg/pipeline/process/handlers/chat.py b/src/langbot/pkg/pipeline/process/handlers/chat.py similarity index 100% rename from pkg/pipeline/process/handlers/chat.py rename to src/langbot/pkg/pipeline/process/handlers/chat.py diff --git a/pkg/pipeline/process/handlers/command.py b/src/langbot/pkg/pipeline/process/handlers/command.py similarity index 100% rename from pkg/pipeline/process/handlers/command.py rename to src/langbot/pkg/pipeline/process/handlers/command.py diff --git a/pkg/pipeline/process/process.py b/src/langbot/pkg/pipeline/process/process.py similarity index 100% rename from pkg/pipeline/process/process.py rename to src/langbot/pkg/pipeline/process/process.py diff --git a/pkg/pipeline/ratelimit/__init__.py b/src/langbot/pkg/pipeline/ratelimit/__init__.py similarity index 100% rename from pkg/pipeline/ratelimit/__init__.py rename to src/langbot/pkg/pipeline/ratelimit/__init__.py diff --git a/pkg/pipeline/ratelimit/algo.py b/src/langbot/pkg/pipeline/ratelimit/algo.py similarity index 100% rename from pkg/pipeline/ratelimit/algo.py rename to src/langbot/pkg/pipeline/ratelimit/algo.py diff --git a/pkg/pipeline/ratelimit/algos/__init__.py b/src/langbot/pkg/pipeline/ratelimit/algos/__init__.py similarity index 100% rename from pkg/pipeline/ratelimit/algos/__init__.py rename to src/langbot/pkg/pipeline/ratelimit/algos/__init__.py diff --git a/pkg/pipeline/ratelimit/algos/fixedwin.py b/src/langbot/pkg/pipeline/ratelimit/algos/fixedwin.py similarity index 100% rename from pkg/pipeline/ratelimit/algos/fixedwin.py rename to src/langbot/pkg/pipeline/ratelimit/algos/fixedwin.py diff --git a/pkg/pipeline/ratelimit/ratelimit.py b/src/langbot/pkg/pipeline/ratelimit/ratelimit.py similarity index 100% rename from pkg/pipeline/ratelimit/ratelimit.py rename to src/langbot/pkg/pipeline/ratelimit/ratelimit.py diff --git a/pkg/pipeline/respback/__init__.py b/src/langbot/pkg/pipeline/respback/__init__.py similarity index 100% rename from pkg/pipeline/respback/__init__.py rename to src/langbot/pkg/pipeline/respback/__init__.py diff --git a/pkg/pipeline/respback/respback.py b/src/langbot/pkg/pipeline/respback/respback.py similarity index 100% rename from pkg/pipeline/respback/respback.py rename to src/langbot/pkg/pipeline/respback/respback.py diff --git a/pkg/pipeline/resprule/__init__.py b/src/langbot/pkg/pipeline/resprule/__init__.py similarity index 100% rename from pkg/pipeline/resprule/__init__.py rename to src/langbot/pkg/pipeline/resprule/__init__.py diff --git a/pkg/pipeline/resprule/entities.py b/src/langbot/pkg/pipeline/resprule/entities.py similarity index 100% rename from pkg/pipeline/resprule/entities.py rename to src/langbot/pkg/pipeline/resprule/entities.py diff --git a/pkg/pipeline/resprule/resprule.py b/src/langbot/pkg/pipeline/resprule/resprule.py similarity index 100% rename from pkg/pipeline/resprule/resprule.py rename to src/langbot/pkg/pipeline/resprule/resprule.py diff --git a/pkg/pipeline/resprule/rule.py b/src/langbot/pkg/pipeline/resprule/rule.py similarity index 100% rename from pkg/pipeline/resprule/rule.py rename to src/langbot/pkg/pipeline/resprule/rule.py diff --git a/pkg/pipeline/resprule/rules/__init__.py b/src/langbot/pkg/pipeline/resprule/rules/__init__.py similarity index 100% rename from pkg/pipeline/resprule/rules/__init__.py rename to src/langbot/pkg/pipeline/resprule/rules/__init__.py diff --git a/pkg/pipeline/resprule/rules/atbot.py b/src/langbot/pkg/pipeline/resprule/rules/atbot.py similarity index 91% rename from pkg/pipeline/resprule/rules/atbot.py rename to src/langbot/pkg/pipeline/resprule/rules/atbot.py index 68c3ace9..9d549d10 100644 --- a/pkg/pipeline/resprule/rules/atbot.py +++ b/src/langbot/pkg/pipeline/resprule/rules/atbot.py @@ -21,7 +21,9 @@ class AtBotRule(rule_model.GroupRespondRule): def remove_at(message_chain: platform_message.MessageChain): nonlocal found for component in message_chain.root: - if isinstance(component, platform_message.At) and str(component.target) == str(query.adapter.bot_account_id): + if isinstance(component, platform_message.At) and str(component.target) == str( + query.adapter.bot_account_id + ): message_chain.remove(component) found = True break diff --git a/pkg/pipeline/resprule/rules/prefix.py b/src/langbot/pkg/pipeline/resprule/rules/prefix.py similarity index 100% rename from pkg/pipeline/resprule/rules/prefix.py rename to src/langbot/pkg/pipeline/resprule/rules/prefix.py diff --git a/pkg/pipeline/resprule/rules/random.py b/src/langbot/pkg/pipeline/resprule/rules/random.py similarity index 100% rename from pkg/pipeline/resprule/rules/random.py rename to src/langbot/pkg/pipeline/resprule/rules/random.py diff --git a/pkg/pipeline/resprule/rules/regexp.py b/src/langbot/pkg/pipeline/resprule/rules/regexp.py similarity index 100% rename from pkg/pipeline/resprule/rules/regexp.py rename to src/langbot/pkg/pipeline/resprule/rules/regexp.py diff --git a/pkg/pipeline/stage.py b/src/langbot/pkg/pipeline/stage.py similarity index 100% rename from pkg/pipeline/stage.py rename to src/langbot/pkg/pipeline/stage.py diff --git a/pkg/pipeline/wrapper/__init__.py b/src/langbot/pkg/pipeline/wrapper/__init__.py similarity index 100% rename from pkg/pipeline/wrapper/__init__.py rename to src/langbot/pkg/pipeline/wrapper/__init__.py diff --git a/pkg/pipeline/wrapper/wrapper.py b/src/langbot/pkg/pipeline/wrapper/wrapper.py similarity index 100% rename from pkg/pipeline/wrapper/wrapper.py rename to src/langbot/pkg/pipeline/wrapper/wrapper.py diff --git a/pkg/platform/__init__.py b/src/langbot/pkg/platform/__init__.py similarity index 100% rename from pkg/platform/__init__.py rename to src/langbot/pkg/platform/__init__.py diff --git a/pkg/platform/botmgr.py b/src/langbot/pkg/platform/botmgr.py similarity index 96% rename from pkg/platform/botmgr.py rename to src/langbot/pkg/platform/botmgr.py index dca24f96..73c59da4 100644 --- a/pkg/platform/botmgr.py +++ b/src/langbot/pkg/platform/botmgr.py @@ -13,7 +13,6 @@ from ..entity.persistence import bot as persistence_bot from ..entity.errors import platform as platform_errors from .logger import EventLogger -from .webhook_pusher import WebhookPusher import langbot_plugin.api.entities.builtin.provider.session as provider_session import langbot_plugin.api.entities.builtin.platform.events as platform_events @@ -70,9 +69,7 @@ class RuntimeBot: # Push to webhooks if hasattr(self.ap, 'webhook_pusher') and self.ap.webhook_pusher: asyncio.create_task( - self.ap.webhook_pusher.push_person_message( - event, self.bot_entity.uuid, adapter.__class__.__name__ - ) + self.ap.webhook_pusher.push_person_message(event, self.bot_entity.uuid, adapter.__class__.__name__) ) await self.ap.query_pool.add_query( @@ -103,9 +100,7 @@ class RuntimeBot: # Push to webhooks if hasattr(self.ap, 'webhook_pusher') and self.ap.webhook_pusher: asyncio.create_task( - self.ap.webhook_pusher.push_group_message( - event, self.bot_entity.uuid, adapter.__class__.__name__ - ) + self.ap.webhook_pusher.push_group_message(event, self.bot_entity.uuid, adapter.__class__.__name__) ) await self.ap.query_pool.add_query( diff --git a/pkg/platform/logger.py b/src/langbot/pkg/platform/logger.py similarity index 100% rename from pkg/platform/logger.py rename to src/langbot/pkg/platform/logger.py diff --git a/pkg/platform/sources/__init__.py b/src/langbot/pkg/platform/sources/__init__.py similarity index 100% rename from pkg/platform/sources/__init__.py rename to src/langbot/pkg/platform/sources/__init__.py diff --git a/pkg/platform/sources/aiocqhttp.py b/src/langbot/pkg/platform/sources/aiocqhttp.py similarity index 100% rename from pkg/platform/sources/aiocqhttp.py rename to src/langbot/pkg/platform/sources/aiocqhttp.py diff --git a/pkg/platform/sources/aiocqhttp.yaml b/src/langbot/pkg/platform/sources/aiocqhttp.yaml similarity index 100% rename from pkg/platform/sources/aiocqhttp.yaml rename to src/langbot/pkg/platform/sources/aiocqhttp.yaml diff --git a/pkg/platform/sources/dingtalk.py b/src/langbot/pkg/platform/sources/dingtalk.py similarity index 95% rename from pkg/platform/sources/dingtalk.py rename to src/langbot/pkg/platform/sources/dingtalk.py index ed468e35..c072a567 100644 --- a/pkg/platform/sources/dingtalk.py +++ b/src/langbot/pkg/platform/sources/dingtalk.py @@ -1,14 +1,13 @@ - import traceback import typing -from libs.dingtalk_api.dingtalkevent import DingTalkEvent +from langbot.libs.dingtalk_api.dingtalkevent import DingTalkEvent import langbot_plugin.api.entities.builtin.platform.message as platform_message import langbot_plugin.api.definition.abstract.platform.adapter as abstract_platform_adapter import langbot_plugin.api.entities.builtin.platform.events as platform_events import langbot_plugin.api.entities.builtin.platform.entities as platform_entities -from libs.dingtalk_api.api import DingTalkClient +from langbot.libs.dingtalk_api.api import DingTalkClient import datetime -from ..logger import EventLogger +from langbot.pkg.platform.logger import EventLogger class DingTalkMessageConverter(abstract_platform_adapter.AbstractMessageConverter): @@ -38,7 +37,7 @@ class DingTalkMessageConverter(abstract_platform_adapter.AbstractMessageConverte yiri_msg_list.append(platform_message.At(target=bot_name)) if event.rich_content: - elements = event.rich_content.get("Elements") + elements = event.rich_content.get('Elements') for element in elements: if element.get('Type') == 'text': text = element.get('Content', '').replace('@' + bot_name, '') @@ -60,8 +59,6 @@ class DingTalkMessageConverter(abstract_platform_adapter.AbstractMessageConverte if event.audio: yiri_msg_list.append(platform_message.Voice(base64=event.audio)) - - chain = platform_message.MessageChain(yiri_msg_list) return chain @@ -122,7 +119,6 @@ class DingTalkAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): ) def __init__(self, config: dict, logger: EventLogger): - required_keys = [ 'client_id', 'client_secret', @@ -133,13 +129,13 @@ class DingTalkAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): if missing_keys: raise Exception('钉钉缺少相关配置项,请查看文档或联系管理员') bot = DingTalkClient( - client_id=config['client_id'], - client_secret=config['client_secret'], - robot_name=config['robot_name'], - robot_code=config['robot_code'], - markdown_card=config['markdown_card'], - logger=logger, - ) + client_id=config['client_id'], + client_secret=config['client_secret'], + robot_name=config['robot_name'], + robot_code=config['robot_code'], + markdown_card=config['markdown_card'], + logger=logger, + ) bot_account_id = config['robot_name'] super().__init__( config=config, @@ -148,7 +144,6 @@ class DingTalkAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): bot_account_id=bot_account_id, bot=bot, listeners={}, - ) async def reply_message( diff --git a/pkg/platform/sources/dingtalk.svg b/src/langbot/pkg/platform/sources/dingtalk.svg similarity index 100% rename from pkg/platform/sources/dingtalk.svg rename to src/langbot/pkg/platform/sources/dingtalk.svg diff --git a/pkg/platform/sources/dingtalk.yaml b/src/langbot/pkg/platform/sources/dingtalk.yaml similarity index 100% rename from pkg/platform/sources/dingtalk.yaml rename to src/langbot/pkg/platform/sources/dingtalk.yaml diff --git a/pkg/platform/sources/discord.py b/src/langbot/pkg/platform/sources/discord.py similarity index 99% rename from pkg/platform/sources/discord.py rename to src/langbot/pkg/platform/sources/discord.py index 98791260..933961de 100644 --- a/pkg/platform/sources/discord.py +++ b/src/langbot/pkg/platform/sources/discord.py @@ -22,7 +22,6 @@ import langbot_plugin.api.definition.abstract.platform.event_logger as abstract_ from ..logger import EventLogger - # 语音功能相关异常定义 class VoiceConnectionError(Exception): """语音连接基础异常""" diff --git a/pkg/platform/sources/discord.svg b/src/langbot/pkg/platform/sources/discord.svg similarity index 100% rename from pkg/platform/sources/discord.svg rename to src/langbot/pkg/platform/sources/discord.svg diff --git a/pkg/platform/sources/discord.yaml b/src/langbot/pkg/platform/sources/discord.yaml similarity index 100% rename from pkg/platform/sources/discord.yaml rename to src/langbot/pkg/platform/sources/discord.yaml diff --git a/pkg/platform/sources/lark.py b/src/langbot/pkg/platform/sources/lark.py similarity index 99% rename from pkg/platform/sources/lark.py rename to src/langbot/pkg/platform/sources/lark.py index 23257e6f..684091a2 100644 --- a/pkg/platform/sources/lark.py +++ b/src/langbot/pkg/platform/sources/lark.py @@ -627,6 +627,7 @@ class LarkAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): except Exception as e: raise e + async def create_message_card(self, message_id, event) -> str: """ 创建卡片消息。 diff --git a/pkg/platform/sources/lark.svg b/src/langbot/pkg/platform/sources/lark.svg similarity index 100% rename from pkg/platform/sources/lark.svg rename to src/langbot/pkg/platform/sources/lark.svg diff --git a/pkg/platform/sources/lark.yaml b/src/langbot/pkg/platform/sources/lark.yaml similarity index 100% rename from pkg/platform/sources/lark.yaml rename to src/langbot/pkg/platform/sources/lark.yaml diff --git a/pkg/platform/sources/legacy/gewechat.png b/src/langbot/pkg/platform/sources/legacy/gewechat.png similarity index 100% rename from pkg/platform/sources/legacy/gewechat.png rename to src/langbot/pkg/platform/sources/legacy/gewechat.png diff --git a/pkg/platform/sources/legacy/gewechat.py b/src/langbot/pkg/platform/sources/legacy/gewechat.py similarity index 100% rename from pkg/platform/sources/legacy/gewechat.py rename to src/langbot/pkg/platform/sources/legacy/gewechat.py diff --git a/pkg/platform/sources/legacy/gewechat.yaml b/src/langbot/pkg/platform/sources/legacy/gewechat.yaml similarity index 100% rename from pkg/platform/sources/legacy/gewechat.yaml rename to src/langbot/pkg/platform/sources/legacy/gewechat.yaml diff --git a/pkg/platform/sources/legacy/nakuru.png b/src/langbot/pkg/platform/sources/legacy/nakuru.png similarity index 100% rename from pkg/platform/sources/legacy/nakuru.png rename to src/langbot/pkg/platform/sources/legacy/nakuru.png diff --git a/pkg/platform/sources/legacy/nakuru.py b/src/langbot/pkg/platform/sources/legacy/nakuru.py similarity index 100% rename from pkg/platform/sources/legacy/nakuru.py rename to src/langbot/pkg/platform/sources/legacy/nakuru.py diff --git a/pkg/platform/sources/legacy/nakuru.yaml b/src/langbot/pkg/platform/sources/legacy/nakuru.yaml similarity index 100% rename from pkg/platform/sources/legacy/nakuru.yaml rename to src/langbot/pkg/platform/sources/legacy/nakuru.yaml diff --git a/pkg/platform/sources/legacy/qqbotpy.py b/src/langbot/pkg/platform/sources/legacy/qqbotpy.py similarity index 100% rename from pkg/platform/sources/legacy/qqbotpy.py rename to src/langbot/pkg/platform/sources/legacy/qqbotpy.py diff --git a/pkg/platform/sources/legacy/qqbotpy.svg b/src/langbot/pkg/platform/sources/legacy/qqbotpy.svg similarity index 100% rename from pkg/platform/sources/legacy/qqbotpy.svg rename to src/langbot/pkg/platform/sources/legacy/qqbotpy.svg diff --git a/pkg/platform/sources/legacy/qqbotpy.yaml b/src/langbot/pkg/platform/sources/legacy/qqbotpy.yaml similarity index 100% rename from pkg/platform/sources/legacy/qqbotpy.yaml rename to src/langbot/pkg/platform/sources/legacy/qqbotpy.yaml diff --git a/pkg/platform/sources/line.png b/src/langbot/pkg/platform/sources/line.png similarity index 100% rename from pkg/platform/sources/line.png rename to src/langbot/pkg/platform/sources/line.png diff --git a/pkg/platform/sources/line.py b/src/langbot/pkg/platform/sources/line.py similarity index 80% rename from pkg/platform/sources/line.py rename to src/langbot/pkg/platform/sources/line.py index 1cbf9850..29ab361e 100644 --- a/pkg/platform/sources/line.py +++ b/src/langbot/pkg/platform/sources/line.py @@ -3,18 +3,11 @@ import quart import traceback -import typing import asyncio -import re import base64 -import uuid -import json import datetime -import hashlib -from Crypto.Cipher import AES -from ...core import app import langbot_plugin.api.definition.abstract.platform.adapter as abstract_platform_adapter import langbot_plugin.api.entities.builtin.platform.message as platform_message import langbot_plugin.api.entities.builtin.platform.events as platform_events @@ -22,30 +15,15 @@ import langbot_plugin.api.entities.builtin.platform.entities as platform_entitie from ..logger import EventLogger - -from linebot.v3 import ( - WebhookHandler -) -from linebot.v3.exceptions import ( - InvalidSignatureError -) -from linebot.v3.messaging import ( - Configuration, - ApiClient, - MessagingApi, - ReplyMessageRequest, - TextMessage, - ImageMessage -) +from linebot.v3 import WebhookHandler +from linebot.v3.exceptions import InvalidSignatureError +from linebot.v3.messaging import Configuration, ApiClient, MessagingApi, ReplyMessageRequest, TextMessage, ImageMessage from linebot.v3.webhooks import ( MessageEvent, TextMessageContent, ImageMessageContent, VideoMessageContent, AudioMessageContent, - FileMessageContent, - LocationMessageContent, - StickerMessageContent ) # from linebot import WebhookParser @@ -53,12 +31,9 @@ from linebot.v3.webhook import WebhookParser from linebot.v3.messaging import MessagingApiBlob - class LINEMessageConverter(abstract_platform_adapter.AbstractMessageConverter): @staticmethod - async def yiri2target( - message_chain: platform_message.MessageChain, api_client: ApiClient - ) -> typing.Tuple[list]: + async def yiri2target(message_chain: platform_message.MessageChain, api_client: ApiClient) -> typing.Tuple[list]: content_list = [] for component in message_chain: if isinstance(component, platform_message.At): @@ -72,15 +47,11 @@ class LINEMessageConverter(abstract_platform_adapter.AbstractMessageConverter): elif isinstance(component, platform_message.Voice): content_list.append({'type': 'voice', 'url': component.url, 'length': component.length}) - return content_list @staticmethod - async def target2yiri( - message, - bot_client - ) -> platform_message.MessageChain: + async def target2yiri(message, bot_client) -> platform_message.MessageChain: lb_msg_list = [] msg_create_time = datetime.datetime.fromtimestamp(int(message.timestamp) / 1000) @@ -99,8 +70,8 @@ class LINEMessageConverter(abstract_platform_adapter.AbstractMessageConverter): # 如果需要Data URI格式(用于直接嵌入HTML等) # 首先需要知道图片类型,LINE图片通常是JPEG - data_uri = f"data:image/jpeg;base64,{base64_string}" - lb_msg_list.append(platform_message.Image(base64 = data_uri)) + data_uri = f'data:image/jpeg;base64,{base64_string}' + lb_msg_list.append(platform_message.Image(base64=data_uri)) return platform_message.MessageChain(lb_msg_list) @@ -112,13 +83,10 @@ class LINEEventConverter(abstract_platform_adapter.AbstractEventConverter): pass @staticmethod - async def target2yiri( - event, - bot_client - ) -> platform_events.Event: + async def target2yiri(event, bot_client) -> platform_events.Event: message_chain = await LINEMessageConverter.target2yiri(event, bot_client) - if event.source.type== 'user': + if event.source.type == 'user': return platform_events.FriendMessage( sender=platform_entities.Friend( id=event.message.id, @@ -150,6 +118,7 @@ class LINEEventConverter(abstract_platform_adapter.AbstractEventConverter): source_platform_object=event, ) + class LINEAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): bot: MessagingApi api_client: ApiClient @@ -166,7 +135,6 @@ class LINEAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): config: dict quart_app: quart.Quart - card_id_dict: dict[str, str] # 消息id到卡片id的映射,便于创建卡片后的发送消息到指定卡片 seq: int # 用于在发送卡片消息中识别消息顺序,直接以seq作为标识 @@ -179,22 +147,21 @@ class LINEAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): bot_account_id = config.get('bot_account_id', 'langbot') - super().__init__( - config = config, - logger = logger, - quart_app = quart.Quart(__name__), - listeners = {}, - card_id_dict = {}, - seq = 1, - event_converter = LINEEventConverter(), - message_converter = LINEMessageConverter(), - line_webhook = line_webhook, - parser = parser, + config=config, + logger=logger, + quart_app=quart.Quart(__name__), + listeners={}, + card_id_dict={}, + seq=1, + event_converter=LINEEventConverter(), + message_converter=LINEMessageConverter(), + line_webhook=line_webhook, + parser=parser, configuration=configuration, - api_client = api_client, - bot = MessagingApi(api_client), - bot_account_id = bot_account_id, + api_client=api_client, + bot=MessagingApi(api_client), + bot_account_id=bot_account_id, ) @self.quart_app.route('/line/callback', methods=['POST']) @@ -205,28 +172,22 @@ class LINEAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): events = parser.parse(body, signature) # 解密解析消息 try: - # print(events) lb_event = await self.event_converter.target2yiri(events[0], self.api_client) if lb_event.__class__ in self.listeners: await self.listeners[lb_event.__class__](lb_event, self) except InvalidSignatureError: - self.logger.info(f"Invalid signature. Please check your channel access token/channel secret.{traceback.format_exc()}") + self.logger.info( + f'Invalid signature. Please check your channel access token/channel secret.{traceback.format_exc()}' + ) return quart.Response('Invalid signature', status=400) - return {'code': 200, 'message': 'ok'} except Exception: await self.logger.error(f'Error in LINE callback: {traceback.format_exc()}') return {'code': 500, 'message': 'error'} - - - - - async def send_message(self, target_type: str, target_id: str, message: platform_message.MessageChain): - pass async def reply_message( @@ -242,14 +203,14 @@ class LINEAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): self.bot.reply_message_with_http_info( ReplyMessageRequest( reply_token=message_source.source_platform_object.reply_token, - messages=[TextMessage(text=content['content'])] + messages=[TextMessage(text=content['content'])], ) ) elif content['type'] == 'image': - self.bot.reply_message_with_http_info( + self.bot.reply_message_with_http_info( ReplyMessageRequest( reply_token=message_source.source_platform_object.reply_token, - messages=[ImageMessage(text=content['content'])] + messages=[ImageMessage(text=content['content'])], ) ) @@ -259,14 +220,18 @@ class LINEAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): def register_listener( self, event_type: typing.Type[platform_events.Event], - callback: typing.Callable[[platform_events.Event, abstract_platform_adapter.AbstractMessagePlatformAdapter], None], + callback: typing.Callable[ + [platform_events.Event, abstract_platform_adapter.AbstractMessagePlatformAdapter], None + ], ): self.listeners[event_type] = callback def unregister_listener( self, event_type: typing.Type[platform_events.Event], - callback: typing.Callable[[platform_events.Event, abstract_platform_adapter.AbstractMessagePlatformAdapter], None], + callback: typing.Callable[ + [platform_events.Event, abstract_platform_adapter.AbstractMessagePlatformAdapter], None + ], ): self.listeners.pop(event_type) @@ -276,6 +241,7 @@ class LINEAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): async def shutdown_trigger_placeholder(): while True: await asyncio.sleep(1) + await self.quart_app.run_task( host='0.0.0.0', port=port, diff --git a/pkg/platform/sources/line.yaml b/src/langbot/pkg/platform/sources/line.yaml similarity index 100% rename from pkg/platform/sources/line.yaml rename to src/langbot/pkg/platform/sources/line.yaml diff --git a/pkg/platform/sources/officialaccount.png b/src/langbot/pkg/platform/sources/officialaccount.png similarity index 100% rename from pkg/platform/sources/officialaccount.png rename to src/langbot/pkg/platform/sources/officialaccount.png diff --git a/pkg/platform/sources/officialaccount.py b/src/langbot/pkg/platform/sources/officialaccount.py similarity index 93% rename from pkg/platform/sources/officialaccount.py rename to src/langbot/pkg/platform/sources/officialaccount.py index 1f70f3eb..7b7f38d9 100644 --- a/pkg/platform/sources/officialaccount.py +++ b/src/langbot/pkg/platform/sources/officialaccount.py @@ -5,14 +5,13 @@ import traceback import pydantic import datetime import langbot_plugin.api.definition.abstract.platform.adapter as abstract_platform_adapter -from libs.official_account_api.oaevent import OAEvent -from libs.official_account_api.api import OAClient -from libs.official_account_api.api import OAClientForLongerResponse +from langbot.libs.official_account_api.oaevent import OAEvent +from langbot.libs.official_account_api.api import OAClient +from langbot.libs.official_account_api.api import OAClientForLongerResponse import langbot_plugin.api.entities.builtin.platform.entities as platform_entities import langbot_plugin.api.entities.builtin.platform.message as platform_message import langbot_plugin.api.entities.builtin.platform.events as platform_events -from langbot_plugin.api.entities.builtin.command import errors as command_errors -from ..logger import EventLogger +from langbot.pkg.platform.logger import EventLogger class OAMessageConverter(abstract_platform_adapter.AbstractMessageConverter): @@ -66,7 +65,6 @@ class OfficialAccountAdapter(abstract_platform_adapter.AbstractMessagePlatformAd if missing_keys: raise Exception(f'OfficialAccount 缺少配置项: {missing_keys}') - if config['Mode'] == 'drop': bot = OAClient( token=config['token'], @@ -89,7 +87,6 @@ class OfficialAccountAdapter(abstract_platform_adapter.AbstractMessagePlatformAd bot_account_id = config.get('AppID', '') - super().__init__( bot=bot, bot_account_id=bot_account_id, @@ -97,10 +94,6 @@ class OfficialAccountAdapter(abstract_platform_adapter.AbstractMessagePlatformAd logger=logger, ) - - - - async def reply_message( self, message_source: platform_events.FriendMessage, @@ -159,5 +152,8 @@ class OfficialAccountAdapter(abstract_platform_adapter.AbstractMessagePlatformAd ): return super().unregister_listener(event_type, callback) - async def is_muted(self, group_id: str, ) -> bool: + async def is_muted( + self, + group_id: str, + ) -> bool: pass diff --git a/pkg/platform/sources/officialaccount.yaml b/src/langbot/pkg/platform/sources/officialaccount.yaml similarity index 100% rename from pkg/platform/sources/officialaccount.yaml rename to src/langbot/pkg/platform/sources/officialaccount.yaml diff --git a/pkg/platform/sources/onebot.png b/src/langbot/pkg/platform/sources/onebot.png similarity index 100% rename from pkg/platform/sources/onebot.png rename to src/langbot/pkg/platform/sources/onebot.png diff --git a/pkg/platform/sources/qqofficial.py b/src/langbot/pkg/platform/sources/qqofficial.py similarity index 95% rename from pkg/platform/sources/qqofficial.py rename to src/langbot/pkg/platform/sources/qqofficial.py index 240b46d0..c0375e16 100644 --- a/pkg/platform/sources/qqofficial.py +++ b/src/langbot/pkg/platform/sources/qqofficial.py @@ -9,11 +9,10 @@ import langbot_plugin.api.definition.abstract.platform.adapter as abstract_platf import langbot_plugin.api.entities.builtin.platform.message as platform_message import langbot_plugin.api.entities.builtin.platform.events as platform_events import langbot_plugin.api.entities.builtin.platform.entities as platform_entities -from langbot_plugin.api.entities.builtin.command import errors as command_errors -from libs.qq_official_api.api import QQOfficialClient -from libs.qq_official_api.qqofficialevent import QQOfficialEvent -from ...utils import image -from ..logger import EventLogger +from langbot.libs.qq_official_api.api import QQOfficialClient +from langbot.libs.qq_official_api.qqofficialevent import QQOfficialEvent +from langbot.pkg.utils import image +from langbot.pkg.platform.logger import EventLogger class QQOfficialMessageConverter(abstract_platform_adapter.AbstractMessageConverter): @@ -139,9 +138,7 @@ class QQOfficialAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter event_converter: QQOfficialEventConverter = QQOfficialEventConverter() def __init__(self, config: dict, logger: EventLogger): - bot = QQOfficialClient( - app_id=config['appid'], secret=config['secret'], token=config['token'], logger=logger - ) + bot = QQOfficialClient(app_id=config['appid'], secret=config['secret'], token=config['token'], logger=logger) super().__init__( config=config, diff --git a/pkg/platform/sources/qqofficial.svg b/src/langbot/pkg/platform/sources/qqofficial.svg similarity index 100% rename from pkg/platform/sources/qqofficial.svg rename to src/langbot/pkg/platform/sources/qqofficial.svg diff --git a/pkg/platform/sources/qqofficial.yaml b/src/langbot/pkg/platform/sources/qqofficial.yaml similarity index 100% rename from pkg/platform/sources/qqofficial.yaml rename to src/langbot/pkg/platform/sources/qqofficial.yaml diff --git a/pkg/platform/sources/slack.png b/src/langbot/pkg/platform/sources/slack.png similarity index 100% rename from pkg/platform/sources/slack.png rename to src/langbot/pkg/platform/sources/slack.png diff --git a/pkg/platform/sources/slack.py b/src/langbot/pkg/platform/sources/slack.py similarity index 97% rename from pkg/platform/sources/slack.py rename to src/langbot/pkg/platform/sources/slack.py index e08cc8c0..dd0ed655 100644 --- a/pkg/platform/sources/slack.py +++ b/src/langbot/pkg/platform/sources/slack.py @@ -5,15 +5,15 @@ import traceback import datetime -from libs.slack_api.api import SlackClient +from langbot.libs.slack_api.api import SlackClient import langbot_plugin.api.definition.abstract.platform.adapter as abstract_platform_adapter -from libs.slack_api.slackevent import SlackEvent +from langbot.libs.slack_api.slackevent import SlackEvent import langbot_plugin.api.entities.builtin.platform.events as platform_events import langbot_plugin.api.entities.builtin.platform.message as platform_message import langbot_plugin.api.entities.builtin.platform.entities as platform_entities from langbot_plugin.api.entities.builtin.command import errors as command_errors -from ...utils import image -from ..logger import EventLogger +from langbot.pkg.utils import image +from langbot.pkg.platform.logger import EventLogger class SlackMessageConverter(abstract_platform_adapter.AbstractMessageConverter): diff --git a/pkg/platform/sources/slack.yaml b/src/langbot/pkg/platform/sources/slack.yaml similarity index 100% rename from pkg/platform/sources/slack.yaml rename to src/langbot/pkg/platform/sources/slack.yaml diff --git a/pkg/platform/sources/telegram.py b/src/langbot/pkg/platform/sources/telegram.py similarity index 100% rename from pkg/platform/sources/telegram.py rename to src/langbot/pkg/platform/sources/telegram.py diff --git a/pkg/platform/sources/telegram.svg b/src/langbot/pkg/platform/sources/telegram.svg similarity index 100% rename from pkg/platform/sources/telegram.svg rename to src/langbot/pkg/platform/sources/telegram.svg diff --git a/pkg/platform/sources/telegram.yaml b/src/langbot/pkg/platform/sources/telegram.yaml similarity index 100% rename from pkg/platform/sources/telegram.yaml rename to src/langbot/pkg/platform/sources/telegram.yaml diff --git a/pkg/platform/sources/webchat.py b/src/langbot/pkg/platform/sources/webchat.py similarity index 100% rename from pkg/platform/sources/webchat.py rename to src/langbot/pkg/platform/sources/webchat.py diff --git a/pkg/platform/sources/webchat.yaml b/src/langbot/pkg/platform/sources/webchat.yaml similarity index 100% rename from pkg/platform/sources/webchat.yaml rename to src/langbot/pkg/platform/sources/webchat.yaml diff --git a/pkg/platform/sources/wechatpad.png b/src/langbot/pkg/platform/sources/wechatpad.png similarity index 100% rename from pkg/platform/sources/wechatpad.png rename to src/langbot/pkg/platform/sources/wechatpad.png diff --git a/pkg/platform/sources/wechatpad.py b/src/langbot/pkg/platform/sources/wechatpad.py similarity index 99% rename from pkg/platform/sources/wechatpad.py rename to src/langbot/pkg/platform/sources/wechatpad.py index 26d735ae..72609cfc 100644 --- a/pkg/platform/sources/wechatpad.py +++ b/src/langbot/pkg/platform/sources/wechatpad.py @@ -4,7 +4,7 @@ import json import time import httpx -from libs.wechatpad_api.client import WeChatPadClient +from langbot.libs.wechatpad_api.client import WeChatPadClient import typing import asyncio @@ -16,7 +16,7 @@ import threading import quart -from ..logger import EventLogger +from langbot.pkg.platform.logger import EventLogger import xml.etree.ElementTree as ET from typing import Optional, Tuple from functools import partial diff --git a/pkg/platform/sources/wechatpad.yaml b/src/langbot/pkg/platform/sources/wechatpad.yaml similarity index 100% rename from pkg/platform/sources/wechatpad.yaml rename to src/langbot/pkg/platform/sources/wechatpad.yaml diff --git a/pkg/platform/sources/wecom.png b/src/langbot/pkg/platform/sources/wecom.png similarity index 100% rename from pkg/platform/sources/wecom.png rename to src/langbot/pkg/platform/sources/wecom.png diff --git a/pkg/platform/sources/wecom.py b/src/langbot/pkg/platform/sources/wecom.py similarity index 96% rename from pkg/platform/sources/wecom.py rename to src/langbot/pkg/platform/sources/wecom.py index 3f5c0676..a009fbd8 100644 --- a/pkg/platform/sources/wecom.py +++ b/src/langbot/pkg/platform/sources/wecom.py @@ -5,12 +5,11 @@ import traceback import datetime -from libs.wecom_api.api import WecomClient +from langbot.libs.wecom_api.api import WecomClient import langbot_plugin.api.definition.abstract.platform.adapter as abstract_platform_adapter -from libs.wecom_api.wecomevent import WecomEvent -from langbot_plugin.api.entities.builtin.command import errors as command_errors -from ...utils import image -from ..logger import EventLogger +from langbot.libs.wecom_api.wecomevent import WecomEvent +from langbot.pkg.utils import image +from langbot.pkg.platform.logger import EventLogger import langbot_plugin.api.entities.builtin.platform.message as platform_message import langbot_plugin.api.entities.builtin.platform.events as platform_events import langbot_plugin.api.entities.builtin.platform.entities as platform_entities @@ -156,15 +155,13 @@ class WecomAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): logger=logger, ) - super().__init__( config=config, logger=logger, bot=bot, - bot_account_id="", + bot_account_id='', ) - async def reply_message( self, message_source: platform_events.MessageEvent, @@ -239,6 +236,6 @@ class WecomAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): ], ): return super().unregister_listener(event_type, callback) - + async def is_muted(self, group_id: int) -> bool: pass diff --git a/pkg/platform/sources/wecom.yaml b/src/langbot/pkg/platform/sources/wecom.yaml similarity index 100% rename from pkg/platform/sources/wecom.yaml rename to src/langbot/pkg/platform/sources/wecom.yaml diff --git a/pkg/platform/sources/wecombot.png b/src/langbot/pkg/platform/sources/wecombot.png similarity index 100% rename from pkg/platform/sources/wecombot.png rename to src/langbot/pkg/platform/sources/wecombot.png diff --git a/pkg/platform/sources/wecombot.py b/src/langbot/pkg/platform/sources/wecombot.py similarity index 90% rename from pkg/platform/sources/wecombot.py rename to src/langbot/pkg/platform/sources/wecombot.py index 13dd8e92..dca8f2c3 100644 --- a/pkg/platform/sources/wecombot.py +++ b/src/langbot/pkg/platform/sources/wecombot.py @@ -8,11 +8,10 @@ import langbot_plugin.api.definition.abstract.platform.adapter as abstract_platf import langbot_plugin.api.entities.builtin.platform.message as platform_message import langbot_plugin.api.entities.builtin.platform.events as platform_events import langbot_plugin.api.entities.builtin.platform.entities as platform_entities -import pydantic -from ..logger import EventLogger -from libs.wecom_ai_bot_api.wecombotevent import WecomBotEvent -from libs.wecom_ai_bot_api.api import WecomBotClient -from ...core import app +from langbot.pkg.platform.logger import EventLogger +from langbot.libs.wecom_ai_bot_api.wecombotevent import WecomBotEvent +from langbot.libs.wecom_ai_bot_api.api import WecomBotClient + class WecomBotMessageConverter(abstract_platform_adapter.AbstractMessageConverter): @staticmethod @@ -36,14 +35,14 @@ class WecomBotMessageConverter(abstract_platform_adapter.AbstractMessageConverte return chain + class WecomBotEventConverter(abstract_platform_adapter.AbstractEventConverter): + @staticmethod + async def yiri2target(event: platform_events.MessageEvent): + return event.source_platform_object @staticmethod - async def yiri2target(event:platform_events.MessageEvent): - return event.source_platform_object - - @staticmethod - async def target2yiri(event:WecomBotEvent): + async def target2yiri(event: WecomBotEvent): message_chain = await WecomBotMessageConverter.target2yiri(event) if event.type == 'single': return platform_events.FriendMessage( @@ -82,6 +81,7 @@ class WecomBotEventConverter(abstract_platform_adapter.AbstractEventConverter): except Exception: print(traceback.format_exc()) + class WecomBotAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): bot: WecomBotClient bot_account_id: str @@ -111,9 +111,12 @@ class WecomBotAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): bot_account_id=bot_account_id, ) - - async def reply_message(self, message_source:platform_events.MessageEvent, message:platform_message.MessageChain,quote_origin: bool = False): - + async def reply_message( + self, + message_source: platform_events.MessageEvent, + message: platform_message.MessageChain, + quote_origin: bool = False, + ): content = await self.message_converter.yiri2target(message) await self.bot.set_message(message_source.source_platform_object.message_id, content) @@ -167,7 +170,9 @@ class WecomBotAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): def register_listener( self, event_type: typing.Type[platform_events.Event], - callback: typing.Callable[[platform_events.Event, abstract_platform_adapter.AbstractMessagePlatformAdapter], None], + callback: typing.Callable[ + [platform_events.Event, abstract_platform_adapter.AbstractMessagePlatformAdapter], None + ], ): async def on_message(event: WecomBotEvent): try: @@ -175,6 +180,7 @@ class WecomBotAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): except Exception: await self.logger.error(f'Error in wecombot callback: {traceback.format_exc()}') print(traceback.format_exc()) + try: if event_type == platform_events.FriendMessage: self.bot.on_message('single')(on_message) @@ -182,7 +188,6 @@ class WecomBotAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): self.bot.on_message('group')(on_message) except Exception: print(traceback.format_exc()) - async def run_async(self): async def shutdown_trigger_placeholder(): @@ -201,11 +206,11 @@ class WecomBotAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): async def unregister_listener( self, event_type: type, - callback: typing.Callable[[platform_events.Event, abstract_platform_adapter.AbstractMessagePlatformAdapter], None], + callback: typing.Callable[ + [platform_events.Event, abstract_platform_adapter.AbstractMessagePlatformAdapter], None + ], ): return super().unregister_listener(event_type, callback) - + async def is_muted(self, group_id: int) -> bool: pass - - diff --git a/pkg/platform/sources/wecombot.yaml b/src/langbot/pkg/platform/sources/wecombot.yaml similarity index 100% rename from pkg/platform/sources/wecombot.yaml rename to src/langbot/pkg/platform/sources/wecombot.yaml diff --git a/pkg/platform/sources/wecomcs.py b/src/langbot/pkg/platform/sources/wecomcs.py similarity index 98% rename from pkg/platform/sources/wecomcs.py rename to src/langbot/pkg/platform/sources/wecomcs.py index 7ce3a064..756daec0 100644 --- a/pkg/platform/sources/wecomcs.py +++ b/src/langbot/pkg/platform/sources/wecomcs.py @@ -6,9 +6,9 @@ import traceback import datetime import pydantic -from libs.wecom_customer_service_api.api import WecomCSClient +from langbot.libs.wecom_customer_service_api.api import WecomCSClient import langbot_plugin.api.definition.abstract.platform.adapter as abstract_platform_adapter -from libs.wecom_customer_service_api.wecomcsevent import WecomCSEvent +from langbot.libs.wecom_customer_service_api.wecomcsevent import WecomCSEvent import langbot_plugin.api.entities.builtin.platform.entities as platform_entities import langbot_plugin.api.entities.builtin.platform.message as platform_message import langbot_plugin.api.entities.builtin.platform.events as platform_events diff --git a/pkg/platform/sources/wecomcs.yaml b/src/langbot/pkg/platform/sources/wecomcs.yaml similarity index 100% rename from pkg/platform/sources/wecomcs.yaml rename to src/langbot/pkg/platform/sources/wecomcs.yaml diff --git a/pkg/platform/webhook_pusher.py b/src/langbot/pkg/platform/webhook_pusher.py similarity index 100% rename from pkg/platform/webhook_pusher.py rename to src/langbot/pkg/platform/webhook_pusher.py diff --git a/pkg/plugin/__init__.py b/src/langbot/pkg/plugin/__init__.py similarity index 100% rename from pkg/plugin/__init__.py rename to src/langbot/pkg/plugin/__init__.py diff --git a/pkg/plugin/connector.py b/src/langbot/pkg/plugin/connector.py similarity index 98% rename from pkg/plugin/connector.py rename to src/langbot/pkg/plugin/connector.py index dea374f6..25223528 100644 --- a/pkg/plugin/connector.py +++ b/src/langbot/pkg/plugin/connector.py @@ -128,13 +128,15 @@ class PluginRuntimeConnector: # See also: https://docs.python.org/zh-cn/3.13/library/asyncio-platforms.html # We have to launch runtime via cmd but communicate via ws. self.ap.logger.info('(windows) use cmd to launch plugin runtime and communicate via ws') - + if self.runtime_subprocess_on_windows is None: # only launch once python_path = sys.executable env = os.environ.copy() self.runtime_subprocess_on_windows = await asyncio.create_subprocess_exec( python_path, - '-m', 'langbot_plugin.cli.__init__', 'rt', + '-m', + 'langbot_plugin.cli.__init__', + 'rt', env=env, ) @@ -150,9 +152,11 @@ class PluginRuntimeConnector: if exc is not None: self.ap.logger.error(f'(windows) Failed to connect to plugin runtime({ws_url}): {exc}') else: - self.ap.logger.error(f'(windows) Failed to connect to plugin runtime({ws_url}), trying to reconnect...') + self.ap.logger.error( + f'(windows) Failed to connect to plugin runtime({ws_url}), trying to reconnect...' + ) await self.runtime_disconnect_callback(self) - + self.ctrl = ws_client_controller.WebSocketClientController( ws_url=ws_url, make_connection_failed_callback=make_connection_failed_callback, diff --git a/pkg/plugin/handler.py b/src/langbot/pkg/plugin/handler.py similarity index 100% rename from pkg/plugin/handler.py rename to src/langbot/pkg/plugin/handler.py diff --git a/pkg/provider/__init__.py b/src/langbot/pkg/provider/__init__.py similarity index 100% rename from pkg/provider/__init__.py rename to src/langbot/pkg/provider/__init__.py diff --git a/pkg/provider/modelmgr/__init__.py b/src/langbot/pkg/provider/modelmgr/__init__.py similarity index 100% rename from pkg/provider/modelmgr/__init__.py rename to src/langbot/pkg/provider/modelmgr/__init__.py diff --git a/pkg/provider/modelmgr/entities.py b/src/langbot/pkg/provider/modelmgr/entities.py similarity index 100% rename from pkg/provider/modelmgr/entities.py rename to src/langbot/pkg/provider/modelmgr/entities.py diff --git a/pkg/provider/modelmgr/errors.py b/src/langbot/pkg/provider/modelmgr/errors.py similarity index 100% rename from pkg/provider/modelmgr/errors.py rename to src/langbot/pkg/provider/modelmgr/errors.py diff --git a/pkg/provider/modelmgr/modelmgr.py b/src/langbot/pkg/provider/modelmgr/modelmgr.py similarity index 100% rename from pkg/provider/modelmgr/modelmgr.py rename to src/langbot/pkg/provider/modelmgr/modelmgr.py diff --git a/pkg/provider/modelmgr/requester.py b/src/langbot/pkg/provider/modelmgr/requester.py similarity index 100% rename from pkg/provider/modelmgr/requester.py rename to src/langbot/pkg/provider/modelmgr/requester.py diff --git a/pkg/provider/modelmgr/requester.yaml b/src/langbot/pkg/provider/modelmgr/requester.yaml similarity index 100% rename from pkg/provider/modelmgr/requester.yaml rename to src/langbot/pkg/provider/modelmgr/requester.yaml diff --git a/pkg/provider/modelmgr/requesters/302ai.png b/src/langbot/pkg/provider/modelmgr/requesters/302ai.png similarity index 100% rename from pkg/provider/modelmgr/requesters/302ai.png rename to src/langbot/pkg/provider/modelmgr/requesters/302ai.png diff --git a/pkg/provider/modelmgr/requesters/302aichatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/302aichatcmpl.py similarity index 100% rename from pkg/provider/modelmgr/requesters/302aichatcmpl.py rename to src/langbot/pkg/provider/modelmgr/requesters/302aichatcmpl.py diff --git a/pkg/provider/modelmgr/requesters/302aichatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/302aichatcmpl.yaml similarity index 100% rename from pkg/provider/modelmgr/requesters/302aichatcmpl.yaml rename to src/langbot/pkg/provider/modelmgr/requesters/302aichatcmpl.yaml diff --git a/pkg/provider/modelmgr/requesters/__init__.py b/src/langbot/pkg/provider/modelmgr/requesters/__init__.py similarity index 100% rename from pkg/provider/modelmgr/requesters/__init__.py rename to src/langbot/pkg/provider/modelmgr/requesters/__init__.py diff --git a/pkg/provider/modelmgr/requesters/anthropic.svg b/src/langbot/pkg/provider/modelmgr/requesters/anthropic.svg similarity index 100% rename from pkg/provider/modelmgr/requesters/anthropic.svg rename to src/langbot/pkg/provider/modelmgr/requesters/anthropic.svg diff --git a/pkg/provider/modelmgr/requesters/anthropicmsgs.py b/src/langbot/pkg/provider/modelmgr/requesters/anthropicmsgs.py similarity index 100% rename from pkg/provider/modelmgr/requesters/anthropicmsgs.py rename to src/langbot/pkg/provider/modelmgr/requesters/anthropicmsgs.py diff --git a/pkg/provider/modelmgr/requesters/anthropicmsgs.yaml b/src/langbot/pkg/provider/modelmgr/requesters/anthropicmsgs.yaml similarity index 100% rename from pkg/provider/modelmgr/requesters/anthropicmsgs.yaml rename to src/langbot/pkg/provider/modelmgr/requesters/anthropicmsgs.yaml diff --git a/pkg/provider/modelmgr/requesters/bailian.png b/src/langbot/pkg/provider/modelmgr/requesters/bailian.png similarity index 100% rename from pkg/provider/modelmgr/requesters/bailian.png rename to src/langbot/pkg/provider/modelmgr/requesters/bailian.png diff --git a/pkg/provider/modelmgr/requesters/bailianchatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/bailianchatcmpl.py similarity index 88% rename from pkg/provider/modelmgr/requesters/bailianchatcmpl.py rename to src/langbot/pkg/provider/modelmgr/requesters/bailianchatcmpl.py index adeaf17f..c60165bb 100644 --- a/pkg/provider/modelmgr/requesters/bailianchatcmpl.py +++ b/src/langbot/pkg/provider/modelmgr/requesters/bailianchatcmpl.py @@ -44,10 +44,10 @@ class BailianChatCompletions(modelscopechatcmpl.ModelScopeChatCompletions): # 设置此次请求中的messages messages = req_messages.copy() - is_use_dashscope_call = False # 是否使用阿里原生库调用 + is_use_dashscope_call = False # 是否使用阿里原生库调用 is_enable_multi_model = True # 是否支持多轮对话 - use_time_num = 0 # 模型已调用次数,防止存在多文件时重复调用 - use_time_ids = [] # 已调用的ID列表 + use_time_num = 0 # 模型已调用次数,防止存在多文件时重复调用 + use_time_ids = [] # 已调用的ID列表 message_id = 0 # 记录消息序号 for msg in messages: @@ -67,20 +67,32 @@ class BailianChatCompletions(modelscopechatcmpl.ModelScopeChatCompletions): me['video_url'] = {'url': me['file_url']} del me['file_url'] del me['file_name'] - use_time_num +=1 + use_time_num += 1 use_time_ids.append(message_id) is_enable_multi_model = False # 2. 语音文件识别, 无法通过openai的audio字段传递,暂时不支持 # https://bailian.console.aliyun.com/?tab=doc#/doc/?type=model&url=2979031 - elif file_type in ['aac', 'amr', 'aiff', 'flac', 'm4a', - 'mp3', 'mpeg', 'ogg', 'opus', 'wav', 'webm', 'wma']: + elif file_type in [ + 'aac', + 'amr', + 'aiff', + 'flac', + 'm4a', + 'mp3', + 'mpeg', + 'ogg', + 'opus', + 'wav', + 'webm', + 'wma', + ]: me['audio'] = me['file_url'] me['type'] = 'audio' del me['file_url'] del me['type'] del me['file_name'] is_use_dashscope_call = True - use_time_num +=1 + use_time_num += 1 use_time_ids.append(message_id) is_enable_multi_model = False message_id += 1 @@ -108,26 +120,26 @@ class BailianChatCompletions(modelscopechatcmpl.ModelScopeChatCompletions): api_key=use_model.token_mgr.get_token(), model=use_model.model_entity.name, messages=messages, - result_format="message", + result_format='message', asr_options={ # "language": "zh", # 可选,若已知音频的语种,可通过该参数指定待识别语种,以提升识别准确率 - "enable_lid": True, - "enable_itn": False + 'enable_lid': True, + 'enable_itn': False, }, - stream=True + stream=True, ) content_length_list = [] previous_length = 0 # 记录上一次的内容长度 for res in response: - chunk = res["output"] + chunk = res['output'] # 解析 chunk 数据 if hasattr(chunk, 'choices') and chunk.choices: choice = chunk.choices[0] - delta_content = choice["message"].content[0]["text"] - finish_reason = choice["finish_reason"] + delta_content = choice['message'].content[0]['text'] + finish_reason = choice['finish_reason'] content_length_list.append(len(delta_content)) else: - delta_content = "" + delta_content = '' finish_reason = None # 跳过空的第一个 chunk(只有 role 没有内容) @@ -137,7 +149,7 @@ class BailianChatCompletions(modelscopechatcmpl.ModelScopeChatCompletions): # 检查 content_length_list 是否有足够的数据 if len(content_length_list) >= 2: - now_content = delta_content[previous_length: content_length_list[-1]] + now_content = delta_content[previous_length : content_length_list[-1]] previous_length = content_length_list[-1] # 更新上一次的长度 else: now_content = delta_content # 第一次循环时直接使用 delta_content @@ -147,7 +159,7 @@ class BailianChatCompletions(modelscopechatcmpl.ModelScopeChatCompletions): chunk_data = { 'role': role, 'content': now_content if now_content else None, - 'is_final': bool(finish_reason) and finish_reason != "null", + 'is_final': bool(finish_reason) and finish_reason != 'null', } # 移除 None 值 diff --git a/pkg/provider/modelmgr/requesters/bailianchatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/bailianchatcmpl.yaml similarity index 100% rename from pkg/provider/modelmgr/requesters/bailianchatcmpl.yaml rename to src/langbot/pkg/provider/modelmgr/requesters/bailianchatcmpl.yaml diff --git a/pkg/provider/modelmgr/requesters/chatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/chatcmpl.py similarity index 100% rename from pkg/provider/modelmgr/requesters/chatcmpl.py rename to src/langbot/pkg/provider/modelmgr/requesters/chatcmpl.py diff --git a/pkg/provider/modelmgr/requesters/chatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/chatcmpl.yaml similarity index 100% rename from pkg/provider/modelmgr/requesters/chatcmpl.yaml rename to src/langbot/pkg/provider/modelmgr/requesters/chatcmpl.yaml diff --git a/pkg/provider/modelmgr/requesters/compshare.png b/src/langbot/pkg/provider/modelmgr/requesters/compshare.png similarity index 100% rename from pkg/provider/modelmgr/requesters/compshare.png rename to src/langbot/pkg/provider/modelmgr/requesters/compshare.png diff --git a/pkg/provider/modelmgr/requesters/compsharechatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/compsharechatcmpl.py similarity index 100% rename from pkg/provider/modelmgr/requesters/compsharechatcmpl.py rename to src/langbot/pkg/provider/modelmgr/requesters/compsharechatcmpl.py diff --git a/pkg/provider/modelmgr/requesters/compsharechatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/compsharechatcmpl.yaml similarity index 100% rename from pkg/provider/modelmgr/requesters/compsharechatcmpl.yaml rename to src/langbot/pkg/provider/modelmgr/requesters/compsharechatcmpl.yaml diff --git a/pkg/provider/modelmgr/requesters/deepseek.svg b/src/langbot/pkg/provider/modelmgr/requesters/deepseek.svg similarity index 100% rename from pkg/provider/modelmgr/requesters/deepseek.svg rename to src/langbot/pkg/provider/modelmgr/requesters/deepseek.svg diff --git a/pkg/provider/modelmgr/requesters/deepseekchatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/deepseekchatcmpl.py similarity index 100% rename from pkg/provider/modelmgr/requesters/deepseekchatcmpl.py rename to src/langbot/pkg/provider/modelmgr/requesters/deepseekchatcmpl.py diff --git a/pkg/provider/modelmgr/requesters/deepseekchatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/deepseekchatcmpl.yaml similarity index 100% rename from pkg/provider/modelmgr/requesters/deepseekchatcmpl.yaml rename to src/langbot/pkg/provider/modelmgr/requesters/deepseekchatcmpl.yaml diff --git a/pkg/provider/modelmgr/requesters/gemini.svg b/src/langbot/pkg/provider/modelmgr/requesters/gemini.svg similarity index 100% rename from pkg/provider/modelmgr/requesters/gemini.svg rename to src/langbot/pkg/provider/modelmgr/requesters/gemini.svg diff --git a/pkg/provider/modelmgr/requesters/geminichatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/geminichatcmpl.py similarity index 100% rename from pkg/provider/modelmgr/requesters/geminichatcmpl.py rename to src/langbot/pkg/provider/modelmgr/requesters/geminichatcmpl.py diff --git a/pkg/provider/modelmgr/requesters/geminichatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/geminichatcmpl.yaml similarity index 100% rename from pkg/provider/modelmgr/requesters/geminichatcmpl.yaml rename to src/langbot/pkg/provider/modelmgr/requesters/geminichatcmpl.yaml diff --git a/pkg/provider/modelmgr/requesters/giteeai.svg b/src/langbot/pkg/provider/modelmgr/requesters/giteeai.svg similarity index 100% rename from pkg/provider/modelmgr/requesters/giteeai.svg rename to src/langbot/pkg/provider/modelmgr/requesters/giteeai.svg diff --git a/pkg/provider/modelmgr/requesters/giteeaichatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/giteeaichatcmpl.py similarity index 100% rename from pkg/provider/modelmgr/requesters/giteeaichatcmpl.py rename to src/langbot/pkg/provider/modelmgr/requesters/giteeaichatcmpl.py diff --git a/pkg/provider/modelmgr/requesters/giteeaichatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/giteeaichatcmpl.yaml similarity index 100% rename from pkg/provider/modelmgr/requesters/giteeaichatcmpl.yaml rename to src/langbot/pkg/provider/modelmgr/requesters/giteeaichatcmpl.yaml diff --git a/pkg/provider/modelmgr/requesters/jiekouai.png b/src/langbot/pkg/provider/modelmgr/requesters/jiekouai.png similarity index 100% rename from pkg/provider/modelmgr/requesters/jiekouai.png rename to src/langbot/pkg/provider/modelmgr/requesters/jiekouai.png diff --git a/pkg/provider/modelmgr/requesters/jiekouaichatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/jiekouaichatcmpl.py similarity index 100% rename from pkg/provider/modelmgr/requesters/jiekouaichatcmpl.py rename to src/langbot/pkg/provider/modelmgr/requesters/jiekouaichatcmpl.py diff --git a/pkg/provider/modelmgr/requesters/jiekouaichatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/jiekouaichatcmpl.yaml similarity index 100% rename from pkg/provider/modelmgr/requesters/jiekouaichatcmpl.yaml rename to src/langbot/pkg/provider/modelmgr/requesters/jiekouaichatcmpl.yaml diff --git a/pkg/provider/modelmgr/requesters/lmstudio.webp b/src/langbot/pkg/provider/modelmgr/requesters/lmstudio.webp similarity index 100% rename from pkg/provider/modelmgr/requesters/lmstudio.webp rename to src/langbot/pkg/provider/modelmgr/requesters/lmstudio.webp diff --git a/pkg/provider/modelmgr/requesters/lmstudiochatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/lmstudiochatcmpl.py similarity index 100% rename from pkg/provider/modelmgr/requesters/lmstudiochatcmpl.py rename to src/langbot/pkg/provider/modelmgr/requesters/lmstudiochatcmpl.py diff --git a/pkg/provider/modelmgr/requesters/lmstudiochatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/lmstudiochatcmpl.yaml similarity index 100% rename from pkg/provider/modelmgr/requesters/lmstudiochatcmpl.yaml rename to src/langbot/pkg/provider/modelmgr/requesters/lmstudiochatcmpl.yaml diff --git a/pkg/provider/modelmgr/requesters/modelscope.svg b/src/langbot/pkg/provider/modelmgr/requesters/modelscope.svg similarity index 100% rename from pkg/provider/modelmgr/requesters/modelscope.svg rename to src/langbot/pkg/provider/modelmgr/requesters/modelscope.svg diff --git a/pkg/provider/modelmgr/requesters/modelscopechatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/modelscopechatcmpl.py similarity index 100% rename from pkg/provider/modelmgr/requesters/modelscopechatcmpl.py rename to src/langbot/pkg/provider/modelmgr/requesters/modelscopechatcmpl.py diff --git a/pkg/provider/modelmgr/requesters/modelscopechatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/modelscopechatcmpl.yaml similarity index 100% rename from pkg/provider/modelmgr/requesters/modelscopechatcmpl.yaml rename to src/langbot/pkg/provider/modelmgr/requesters/modelscopechatcmpl.yaml diff --git a/pkg/provider/modelmgr/requesters/moonshot.png b/src/langbot/pkg/provider/modelmgr/requesters/moonshot.png similarity index 100% rename from pkg/provider/modelmgr/requesters/moonshot.png rename to src/langbot/pkg/provider/modelmgr/requesters/moonshot.png diff --git a/pkg/provider/modelmgr/requesters/moonshotchatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/moonshotchatcmpl.py similarity index 100% rename from pkg/provider/modelmgr/requesters/moonshotchatcmpl.py rename to src/langbot/pkg/provider/modelmgr/requesters/moonshotchatcmpl.py diff --git a/pkg/provider/modelmgr/requesters/moonshotchatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/moonshotchatcmpl.yaml similarity index 100% rename from pkg/provider/modelmgr/requesters/moonshotchatcmpl.yaml rename to src/langbot/pkg/provider/modelmgr/requesters/moonshotchatcmpl.yaml diff --git a/pkg/provider/modelmgr/requesters/newapi.png b/src/langbot/pkg/provider/modelmgr/requesters/newapi.png similarity index 100% rename from pkg/provider/modelmgr/requesters/newapi.png rename to src/langbot/pkg/provider/modelmgr/requesters/newapi.png diff --git a/pkg/provider/modelmgr/requesters/newapichatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/newapichatcmpl.py similarity index 100% rename from pkg/provider/modelmgr/requesters/newapichatcmpl.py rename to src/langbot/pkg/provider/modelmgr/requesters/newapichatcmpl.py diff --git a/pkg/provider/modelmgr/requesters/newapichatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/newapichatcmpl.yaml similarity index 100% rename from pkg/provider/modelmgr/requesters/newapichatcmpl.yaml rename to src/langbot/pkg/provider/modelmgr/requesters/newapichatcmpl.yaml diff --git a/pkg/provider/modelmgr/requesters/ollama.svg b/src/langbot/pkg/provider/modelmgr/requesters/ollama.svg similarity index 100% rename from pkg/provider/modelmgr/requesters/ollama.svg rename to src/langbot/pkg/provider/modelmgr/requesters/ollama.svg diff --git a/pkg/provider/modelmgr/requesters/ollamachat.py b/src/langbot/pkg/provider/modelmgr/requesters/ollamachat.py similarity index 100% rename from pkg/provider/modelmgr/requesters/ollamachat.py rename to src/langbot/pkg/provider/modelmgr/requesters/ollamachat.py diff --git a/pkg/provider/modelmgr/requesters/ollamachat.yaml b/src/langbot/pkg/provider/modelmgr/requesters/ollamachat.yaml similarity index 100% rename from pkg/provider/modelmgr/requesters/ollamachat.yaml rename to src/langbot/pkg/provider/modelmgr/requesters/ollamachat.yaml diff --git a/pkg/provider/modelmgr/requesters/openai.svg b/src/langbot/pkg/provider/modelmgr/requesters/openai.svg similarity index 100% rename from pkg/provider/modelmgr/requesters/openai.svg rename to src/langbot/pkg/provider/modelmgr/requesters/openai.svg diff --git a/pkg/provider/modelmgr/requesters/openrouter.svg b/src/langbot/pkg/provider/modelmgr/requesters/openrouter.svg similarity index 100% rename from pkg/provider/modelmgr/requesters/openrouter.svg rename to src/langbot/pkg/provider/modelmgr/requesters/openrouter.svg diff --git a/pkg/provider/modelmgr/requesters/openrouterchatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/openrouterchatcmpl.py similarity index 100% rename from pkg/provider/modelmgr/requesters/openrouterchatcmpl.py rename to src/langbot/pkg/provider/modelmgr/requesters/openrouterchatcmpl.py diff --git a/pkg/provider/modelmgr/requesters/openrouterchatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/openrouterchatcmpl.yaml similarity index 100% rename from pkg/provider/modelmgr/requesters/openrouterchatcmpl.yaml rename to src/langbot/pkg/provider/modelmgr/requesters/openrouterchatcmpl.yaml diff --git a/pkg/provider/modelmgr/requesters/ppio.svg b/src/langbot/pkg/provider/modelmgr/requesters/ppio.svg similarity index 100% rename from pkg/provider/modelmgr/requesters/ppio.svg rename to src/langbot/pkg/provider/modelmgr/requesters/ppio.svg diff --git a/pkg/provider/modelmgr/requesters/ppiochatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/ppiochatcmpl.py similarity index 100% rename from pkg/provider/modelmgr/requesters/ppiochatcmpl.py rename to src/langbot/pkg/provider/modelmgr/requesters/ppiochatcmpl.py diff --git a/pkg/provider/modelmgr/requesters/ppiochatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/ppiochatcmpl.yaml similarity index 100% rename from pkg/provider/modelmgr/requesters/ppiochatcmpl.yaml rename to src/langbot/pkg/provider/modelmgr/requesters/ppiochatcmpl.yaml diff --git a/pkg/provider/modelmgr/requesters/qhaigc.png b/src/langbot/pkg/provider/modelmgr/requesters/qhaigc.png similarity index 100% rename from pkg/provider/modelmgr/requesters/qhaigc.png rename to src/langbot/pkg/provider/modelmgr/requesters/qhaigc.png diff --git a/pkg/provider/modelmgr/requesters/qhaigcchatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/qhaigcchatcmpl.py similarity index 100% rename from pkg/provider/modelmgr/requesters/qhaigcchatcmpl.py rename to src/langbot/pkg/provider/modelmgr/requesters/qhaigcchatcmpl.py diff --git a/pkg/provider/modelmgr/requesters/qhaigcchatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/qhaigcchatcmpl.yaml similarity index 100% rename from pkg/provider/modelmgr/requesters/qhaigcchatcmpl.yaml rename to src/langbot/pkg/provider/modelmgr/requesters/qhaigcchatcmpl.yaml diff --git a/pkg/provider/modelmgr/requesters/shengsuanyun.py b/src/langbot/pkg/provider/modelmgr/requesters/shengsuanyun.py similarity index 100% rename from pkg/provider/modelmgr/requesters/shengsuanyun.py rename to src/langbot/pkg/provider/modelmgr/requesters/shengsuanyun.py diff --git a/pkg/provider/modelmgr/requesters/shengsuanyun.svg b/src/langbot/pkg/provider/modelmgr/requesters/shengsuanyun.svg similarity index 100% rename from pkg/provider/modelmgr/requesters/shengsuanyun.svg rename to src/langbot/pkg/provider/modelmgr/requesters/shengsuanyun.svg diff --git a/pkg/provider/modelmgr/requesters/shengsuanyun.yaml b/src/langbot/pkg/provider/modelmgr/requesters/shengsuanyun.yaml similarity index 100% rename from pkg/provider/modelmgr/requesters/shengsuanyun.yaml rename to src/langbot/pkg/provider/modelmgr/requesters/shengsuanyun.yaml diff --git a/pkg/provider/modelmgr/requesters/siliconflow.svg b/src/langbot/pkg/provider/modelmgr/requesters/siliconflow.svg similarity index 100% rename from pkg/provider/modelmgr/requesters/siliconflow.svg rename to src/langbot/pkg/provider/modelmgr/requesters/siliconflow.svg diff --git a/pkg/provider/modelmgr/requesters/siliconflowchatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/siliconflowchatcmpl.py similarity index 100% rename from pkg/provider/modelmgr/requesters/siliconflowchatcmpl.py rename to src/langbot/pkg/provider/modelmgr/requesters/siliconflowchatcmpl.py diff --git a/pkg/provider/modelmgr/requesters/siliconflowchatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/siliconflowchatcmpl.yaml similarity index 100% rename from pkg/provider/modelmgr/requesters/siliconflowchatcmpl.yaml rename to src/langbot/pkg/provider/modelmgr/requesters/siliconflowchatcmpl.yaml diff --git a/pkg/provider/modelmgr/requesters/tokenpony.svg b/src/langbot/pkg/provider/modelmgr/requesters/tokenpony.svg similarity index 100% rename from pkg/provider/modelmgr/requesters/tokenpony.svg rename to src/langbot/pkg/provider/modelmgr/requesters/tokenpony.svg diff --git a/pkg/provider/modelmgr/requesters/tokenpony.yaml b/src/langbot/pkg/provider/modelmgr/requesters/tokenpony.yaml similarity index 100% rename from pkg/provider/modelmgr/requesters/tokenpony.yaml rename to src/langbot/pkg/provider/modelmgr/requesters/tokenpony.yaml diff --git a/pkg/provider/modelmgr/requesters/tokenponychatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/tokenponychatcmpl.py similarity index 100% rename from pkg/provider/modelmgr/requesters/tokenponychatcmpl.py rename to src/langbot/pkg/provider/modelmgr/requesters/tokenponychatcmpl.py diff --git a/pkg/provider/modelmgr/requesters/volcark.svg b/src/langbot/pkg/provider/modelmgr/requesters/volcark.svg similarity index 100% rename from pkg/provider/modelmgr/requesters/volcark.svg rename to src/langbot/pkg/provider/modelmgr/requesters/volcark.svg diff --git a/pkg/provider/modelmgr/requesters/volcarkchatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/volcarkchatcmpl.py similarity index 100% rename from pkg/provider/modelmgr/requesters/volcarkchatcmpl.py rename to src/langbot/pkg/provider/modelmgr/requesters/volcarkchatcmpl.py diff --git a/pkg/provider/modelmgr/requesters/volcarkchatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/volcarkchatcmpl.yaml similarity index 100% rename from pkg/provider/modelmgr/requesters/volcarkchatcmpl.yaml rename to src/langbot/pkg/provider/modelmgr/requesters/volcarkchatcmpl.yaml diff --git a/pkg/provider/modelmgr/requesters/xai.svg b/src/langbot/pkg/provider/modelmgr/requesters/xai.svg similarity index 100% rename from pkg/provider/modelmgr/requesters/xai.svg rename to src/langbot/pkg/provider/modelmgr/requesters/xai.svg diff --git a/pkg/provider/modelmgr/requesters/xaichatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/xaichatcmpl.py similarity index 100% rename from pkg/provider/modelmgr/requesters/xaichatcmpl.py rename to src/langbot/pkg/provider/modelmgr/requesters/xaichatcmpl.py diff --git a/pkg/provider/modelmgr/requesters/xaichatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/xaichatcmpl.yaml similarity index 100% rename from pkg/provider/modelmgr/requesters/xaichatcmpl.yaml rename to src/langbot/pkg/provider/modelmgr/requesters/xaichatcmpl.yaml diff --git a/pkg/provider/modelmgr/requesters/zhipuai.svg b/src/langbot/pkg/provider/modelmgr/requesters/zhipuai.svg similarity index 100% rename from pkg/provider/modelmgr/requesters/zhipuai.svg rename to src/langbot/pkg/provider/modelmgr/requesters/zhipuai.svg diff --git a/pkg/provider/modelmgr/requesters/zhipuaichatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/zhipuaichatcmpl.py similarity index 100% rename from pkg/provider/modelmgr/requesters/zhipuaichatcmpl.py rename to src/langbot/pkg/provider/modelmgr/requesters/zhipuaichatcmpl.py diff --git a/pkg/provider/modelmgr/requesters/zhipuaichatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/zhipuaichatcmpl.yaml similarity index 100% rename from pkg/provider/modelmgr/requesters/zhipuaichatcmpl.yaml rename to src/langbot/pkg/provider/modelmgr/requesters/zhipuaichatcmpl.yaml diff --git a/pkg/provider/modelmgr/token.py b/src/langbot/pkg/provider/modelmgr/token.py similarity index 100% rename from pkg/provider/modelmgr/token.py rename to src/langbot/pkg/provider/modelmgr/token.py diff --git a/pkg/provider/runner.py b/src/langbot/pkg/provider/runner.py similarity index 70% rename from pkg/provider/runner.py rename to src/langbot/pkg/provider/runner.py index 83acfe13..f89c079d 100644 --- a/pkg/provider/runner.py +++ b/src/langbot/pkg/provider/runner.py @@ -4,8 +4,6 @@ import abc import typing from ..core import app -import langbot_plugin.api.entities.builtin.provider.message as provider_message -import langbot_plugin.api.entities.builtin.pipeline.query as pipeline_query preregistered_runners: list[typing.Type[RequestRunner]] = [] @@ -36,6 +34,8 @@ class RequestRunner(abc.ABC): self.pipeline_config = pipeline_config @abc.abstractmethod - async def run(self, query: core_entities.Query) -> typing.AsyncGenerator[llm_entities.Message | llm_entities.MessageChunk, None]: + async def run( + self, query: core_entities.Query + ) -> typing.AsyncGenerator[llm_entities.Message | llm_entities.MessageChunk, None]: """运行请求""" - pass \ No newline at end of file + pass diff --git a/pkg/provider/runners/__init__.py b/src/langbot/pkg/provider/runners/__init__.py similarity index 100% rename from pkg/provider/runners/__init__.py rename to src/langbot/pkg/provider/runners/__init__.py diff --git a/pkg/provider/runners/cozeapi.py b/src/langbot/pkg/provider/runners/cozeapi.py similarity index 77% rename from pkg/provider/runners/cozeapi.py rename to src/langbot/pkg/provider/runners/cozeapi.py index 0fdb6f9b..26980f81 100644 --- a/pkg/provider/runners/cozeapi.py +++ b/src/langbot/pkg/provider/runners/cozeapi.py @@ -2,15 +2,15 @@ from __future__ import annotations import typing import json -import uuid import base64 -from .. import runner -from ...core import app +from langbot.pkg.provider import runner +from langbot.pkg.core import app import langbot_plugin.api.entities.builtin.provider.message as provider_message -from ...utils import image +from langbot.pkg.utils import image import langbot_plugin.api.entities.builtin.pipeline.query as pipeline_query -from libs.coze_server_api.client import AsyncCozeAPIClient +from langbot.libs.coze_server_api.client import AsyncCozeAPIClient + @runner.runner_class('coze-api') class CozeAPIRunner(runner.RequestRunner): @@ -19,17 +19,14 @@ class CozeAPIRunner(runner.RequestRunner): def __init__(self, ap: app.Application, pipeline_config: dict): self.pipeline_config = pipeline_config self.ap = ap - self.agent_token = pipeline_config["ai"]['coze-api']['api-key'] - self.bot_id = pipeline_config["ai"]['coze-api'].get('bot-id') - self.chat_timeout = pipeline_config["ai"]['coze-api'].get('timeout') - self.auto_save_history = pipeline_config["ai"]['coze-api'].get('auto_save_history') - self.api_base = pipeline_config["ai"]['coze-api'].get('api-base') + self.agent_token = pipeline_config['ai']['coze-api']['api-key'] + self.bot_id = pipeline_config['ai']['coze-api'].get('bot-id') + self.chat_timeout = pipeline_config['ai']['coze-api'].get('timeout') + self.auto_save_history = pipeline_config['ai']['coze-api'].get('auto_save_history') + self.api_base = pipeline_config['ai']['coze-api'].get('api-base') + + self.coze = AsyncCozeAPIClient(self.agent_token, self.api_base) - self.coze = AsyncCozeAPIClient( - self.agent_token, - self.api_base - ) - def _process_thinking_content( self, content: str, @@ -62,7 +59,7 @@ class CozeAPIRunner(runner.RequestRunner): if thinking_content: content = f'\n{thinking_content}\n\n{content}'.strip() return content, thinking_content - + async def _preprocess_user_message(self, query: pipeline_query.Query) -> list[dict]: """预处理用户消息,转换为Coze消息格式 @@ -70,44 +67,43 @@ class CozeAPIRunner(runner.RequestRunner): list[dict]: Coze消息列表 """ messages = [] - + if isinstance(query.user_message.content, list): # 多模态消息处理 content_parts = [] - + for ce in query.user_message.content: if ce.type == 'text': - content_parts.append({"type": "text", "text": ce.text}) + content_parts.append({'type': 'text', 'text': ce.text}) elif ce.type == 'image_base64': image_b64, image_format = await image.extract_b64_and_format(ce.image_base64) file_bytes = base64.b64decode(image_b64) file_id = await self._get_file_id(file_bytes) - content_parts.append({"type": "image", "file_id": file_id}) + content_parts.append({'type': 'image', 'file_id': file_id}) elif ce.type == 'file': # 处理文件,上传到Coze file_id = await self._get_file_id(ce.file) - content_parts.append({"type": "file", "file_id": file_id}) - + content_parts.append({'type': 'file', 'file_id': file_id}) + # 创建多模态消息 if content_parts: - messages.append({ - "role": "user", - "content": json.dumps(content_parts), - "content_type": "object_string", - "meta_data": None - }) - + messages.append( + { + 'role': 'user', + 'content': json.dumps(content_parts), + 'content_type': 'object_string', + 'meta_data': None, + } + ) + elif isinstance(query.user_message.content, str): # 纯文本消息 - messages.append({ - "role": "user", - "content": query.user_message.content, - "content_type": "text", - "meta_data": None - }) - + messages.append( + {'role': 'user', 'content': query.user_message.content, 'content_type': 'text', 'meta_data': None} + ) + return messages - + async def _get_file_id(self, file) -> str: """上传文件到Coze服务 Args: @@ -122,21 +118,21 @@ class CozeAPIRunner(runner.RequestRunner): self, query: pipeline_query.Query ) -> typing.AsyncGenerator[provider_message.Message, None]: """调用聊天助手(非流式) - + 注意:由于cozepy没有提供非流式API,这里使用流式API并在结束后一次性返回完整内容 """ user_id = f'{query.launcher_type.value}_{query.launcher_id}' # 预处理用户消息 additional_messages = await self._preprocess_user_message(query) - + # 获取会话ID conversation_id = None - + # 收集完整内容 full_content = '' full_reasoning = '' - + try: # 调用Coze API流式接口 async for chunk in self.coze.chat_messages( @@ -146,54 +142,54 @@ class CozeAPIRunner(runner.RequestRunner): conversation_id=conversation_id, timeout=self.chat_timeout, auto_save_history=self.auto_save_history, - stream=True + stream=True, ): self.ap.logger.debug(f'coze-chat-stream: {chunk}') - + event_type = chunk.get('event') data = chunk.get('data', {}) # Removed debug print statement to avoid cluttering logs in production - + if event_type == 'conversation.message.delta': # 收集内容 if 'content' in data: full_content += data.get('content', '') - + # 收集推理内容(如果有) if 'reasoning_content' in data: full_reasoning += data.get('reasoning_content', '') - - elif event_type.split(".")[-1] == 'done' : # 本地部署coze时,结束event不为done + + elif event_type.split('.')[-1] == 'done': # 本地部署coze时,结束event不为done # 保存会话ID if 'conversation_id' in data: conversation_id = data.get('conversation_id') - + elif event_type == 'error': # 处理错误 - error_msg = f"Coze API错误: {data.get('message', '未知错误')}" + error_msg = f'Coze API错误: {data.get("message", "未知错误")}' yield provider_message.Message( role='assistant', content=error_msg, ) return - + # 处理思维链内容 content, thinking_content = self._process_thinking_content(full_content) if full_reasoning: remove_think = self.pipeline_config.get('output', {}).get('misc', {}).get('remove-think', False) if not remove_think: content = f'\n{full_reasoning}\n\n{content}'.strip() - + # 一次性返回完整内容 yield provider_message.Message( role='assistant', content=content, ) - + # 保存会话ID if conversation_id and query.session.using_conversation: query.session.using_conversation.uuid = conversation_id - + except Exception as e: self.ap.logger.error(f'Coze API错误: {str(e)}') yield provider_message.Message( @@ -201,7 +197,6 @@ class CozeAPIRunner(runner.RequestRunner): content=f'Coze API调用失败: {str(e)}', ) - async def _chat_messages_chunk( self, query: pipeline_query.Query ) -> typing.AsyncGenerator[provider_message.MessageChunk, None]: @@ -221,8 +216,6 @@ class CozeAPIRunner(runner.RequestRunner): full_content = '' remove_think = self.pipeline_config.get('output', {}).get('misc', {}).get('remove-think', False) - - try: # 调用Coze API流式接口 async for chunk in self.coze.chat_messages( @@ -232,22 +225,21 @@ class CozeAPIRunner(runner.RequestRunner): conversation_id=conversation_id, timeout=self.chat_timeout, auto_save_history=self.auto_save_history, - stream=True + stream=True, ): self.ap.logger.debug(f'coze-chat-stream-chunk: {chunk}') event_type = chunk.get('event') data = chunk.get('data', {}) - content = "" - + content = '' + if event_type == 'conversation.message.delta': message_idx += 1 # 处理内容增量 - if "reasoning_content" in data and not remove_think: - + if 'reasoning_content' in data and not remove_think: reasoning_content = data.get('reasoning_content', '') if reasoning_content and not start_reasoning: - content = f"\n" + content = '\n' start_reasoning = True content += reasoning_content @@ -255,11 +247,10 @@ class CozeAPIRunner(runner.RequestRunner): if data.get('content', ''): content += data.get('content', '') if not stop_reasoning and start_reasoning: - content = f"\n{content}" + content = f'\n{content}' stop_reasoning = True - - elif event_type.split(".")[-1] == 'done' : # 本地部署coze时,结束event不为done + elif event_type.split('.')[-1] == 'done': # 本地部署coze时,结束event不为done # 保存会话ID if 'conversation_id' in data: conversation_id = data.get('conversation_id') @@ -267,34 +258,22 @@ class CozeAPIRunner(runner.RequestRunner): query.session.using_conversation.uuid = conversation_id is_final = True - elif event_type == 'error': # 处理错误 - error_msg = f"Coze API错误: {data.get('message', '未知错误')}" - yield provider_message.MessageChunk( - role='assistant', - content=error_msg, - finish_reason='error' - ) + error_msg = f'Coze API错误: {data.get("message", "未知错误")}' + yield provider_message.MessageChunk(role='assistant', content=error_msg, finish_reason='error') return full_content += content if message_idx % 8 == 0 or is_final: if full_content: - yield provider_message.MessageChunk( - role='assistant', - content=full_content, - is_final=is_final - ) - + yield provider_message.MessageChunk(role='assistant', content=full_content, is_final=is_final) + except Exception as e: self.ap.logger.error(f'Coze API流式调用错误: {str(e)}') yield provider_message.MessageChunk( - role='assistant', - content=f'Coze API流式调用失败: {str(e)}', - finish_reason='error' + role='assistant', content=f'Coze API流式调用失败: {str(e)}', finish_reason='error' ) - async def run(self, query: pipeline_query.Query) -> typing.AsyncGenerator[provider_message.Message, None]: """运行""" msg_seq = 0 @@ -307,7 +286,3 @@ class CozeAPIRunner(runner.RequestRunner): else: async for msg in self._chat_messages(query): yield msg - - - - diff --git a/pkg/provider/runners/dashscopeapi.py b/src/langbot/pkg/provider/runners/dashscopeapi.py similarity index 100% rename from pkg/provider/runners/dashscopeapi.py rename to src/langbot/pkg/provider/runners/dashscopeapi.py diff --git a/pkg/provider/runners/difysvapi.py b/src/langbot/pkg/provider/runners/difysvapi.py similarity index 99% rename from pkg/provider/runners/difysvapi.py rename to src/langbot/pkg/provider/runners/difysvapi.py index eefc7524..21fb471e 100644 --- a/pkg/provider/runners/difysvapi.py +++ b/src/langbot/pkg/provider/runners/difysvapi.py @@ -6,13 +6,12 @@ import uuid import base64 -from .. import runner -from ...core import app +from langbot.pkg.provider import runner +from langbot.pkg.core import app import langbot_plugin.api.entities.builtin.provider.message as provider_message -from ...utils import image +from langbot.pkg.utils import image import langbot_plugin.api.entities.builtin.pipeline.query as pipeline_query -from libs.dify_service_api.v1 import client, errors - +from langbot.libs.dify_service_api.v1 import client, errors @runner.runner_class('dify-service-api') diff --git a/pkg/provider/runners/langflowapi.py b/src/langbot/pkg/provider/runners/langflowapi.py similarity index 100% rename from pkg/provider/runners/langflowapi.py rename to src/langbot/pkg/provider/runners/langflowapi.py diff --git a/pkg/provider/runners/localagent.py b/src/langbot/pkg/provider/runners/localagent.py similarity index 99% rename from pkg/provider/runners/localagent.py rename to src/langbot/pkg/provider/runners/localagent.py index f89a4e1c..6375ca31 100644 --- a/pkg/provider/runners/localagent.py +++ b/src/langbot/pkg/provider/runners/localagent.py @@ -42,7 +42,7 @@ class LocalAgentRunner(runner.RequestRunner): # Get knowledge bases list (new field) kb_uuids = query.pipeline_config['ai']['local-agent'].get('knowledge-bases', []) - + # Fallback to old field for backward compatibility if not kb_uuids: old_kb_uuid = query.pipeline_config['ai']['local-agent'].get('knowledge-base', '') @@ -64,7 +64,7 @@ class LocalAgentRunner(runner.RequestRunner): if kb_uuids and user_message_text: # only support text for now all_results = [] - + # Retrieve from each knowledge base for kb_uuid in kb_uuids: kb = await self.ap.rag_mgr.get_knowledge_base_by_uuid(kb_uuid) @@ -74,7 +74,7 @@ class LocalAgentRunner(runner.RequestRunner): continue result = await kb.retrieve(user_message_text, kb.knowledge_base_entity.top_k) - + if result: all_results.extend(result) diff --git a/pkg/provider/runners/n8nsvapi.py b/src/langbot/pkg/provider/runners/n8nsvapi.py similarity index 100% rename from pkg/provider/runners/n8nsvapi.py rename to src/langbot/pkg/provider/runners/n8nsvapi.py diff --git a/pkg/provider/runners/tboxapi.py b/src/langbot/pkg/provider/runners/tboxapi.py similarity index 93% rename from pkg/provider/runners/tboxapi.py rename to src/langbot/pkg/provider/runners/tboxapi.py index f0b1bd6a..0fb22a64 100644 --- a/pkg/provider/runners/tboxapi.py +++ b/src/langbot/pkg/provider/runners/tboxapi.py @@ -65,10 +65,8 @@ class TboxAPIRunner(runner.RequestRunner): with tempfile.NamedTemporaryFile(suffix=f'.{image_format}', delete=False) as tmp_file: tmp_file.write(file_bytes) tmp_file_path = tmp_file.name - file_upload_resp = self.tbox_client.upload_file( - tmp_file_path - ) - image_id = file_upload_resp.get("data", "") + file_upload_resp = self.tbox_client.upload_file(tmp_file_path) + image_id = file_upload_resp.get('data', '') image_ids.append(image_id) finally: # 清理临时文件 @@ -97,15 +95,12 @@ class TboxAPIRunner(runner.RequestRunner): files = None if image_ids: - files = [ - File(file_id=image_id, type=FileType.IMAGE) - for image_id in image_ids - ] + files = [File(file_id=image_id, type=FileType.IMAGE) for image_id in image_ids] # 发送对话请求 response = self.tbox_client.chat( app_id=self.app_id, # Tbox中智能体应用的ID - user_id=query.bot_uuid, # 用户ID + user_id=query.bot_uuid, # 用户ID query=plain_text, # 用户输入的文本信息 stream=is_stream, # 是否流式输出 conversation_id=conversation_id, # 会话ID,为None时Tbox会自动创建一个新会话 @@ -124,13 +119,13 @@ class TboxAPIRunner(runner.RequestRunner): ) def _process_non_stream_message(self, response: typing.Dict, query: pipeline_query.Query, remove_think: bool): - if response.get('errorCode') != "0": + if response.get('errorCode') != '0': raise TboxAPIError(f'Tbox API 请求失败: {response.get("errorMsg", "")}') payload = response.get('data', {}) conversation_id = payload.get('conversationId', '') query.session.using_conversation.uuid = conversation_id thinking_content = payload.get('reasoningContent', []) - result = "" + result = '' if thinking_content and not remove_think: result += f'\n{thinking_content[0].get("text", "")}\n\n' content = payload.get('result', []) @@ -138,7 +133,9 @@ class TboxAPIRunner(runner.RequestRunner): result += content[0].get('chunk', '') return result - def _process_stream_message(self, response: typing.Generator[dict], query: pipeline_query.Query, remove_think: bool): + def _process_stream_message( + self, response: typing.Generator[dict], query: pipeline_query.Query, remove_think: bool + ): idx_msg = 0 pending_content = '' conversation_id = None @@ -170,7 +167,7 @@ class TboxAPIRunner(runner.RequestRunner): payload = json.loads(chunk.get('payload', '{}')) if payload.get('ext_data', {}).get('text'): idx_msg += 1 - content = payload.get('ext_data', {}).get('text') + content = payload.get('ext_data', {}).get('text') if not think_start: think_start = True pending_content += f'\n{content}' diff --git a/pkg/provider/session/__init__.py b/src/langbot/pkg/provider/session/__init__.py similarity index 100% rename from pkg/provider/session/__init__.py rename to src/langbot/pkg/provider/session/__init__.py diff --git a/pkg/provider/session/sessionmgr.py b/src/langbot/pkg/provider/session/sessionmgr.py similarity index 100% rename from pkg/provider/session/sessionmgr.py rename to src/langbot/pkg/provider/session/sessionmgr.py diff --git a/pkg/provider/tools/__init__.py b/src/langbot/pkg/provider/tools/__init__.py similarity index 100% rename from pkg/provider/tools/__init__.py rename to src/langbot/pkg/provider/tools/__init__.py diff --git a/pkg/provider/tools/loader.py b/src/langbot/pkg/provider/tools/loader.py similarity index 100% rename from pkg/provider/tools/loader.py rename to src/langbot/pkg/provider/tools/loader.py diff --git a/pkg/provider/tools/loaders/__init__.py b/src/langbot/pkg/provider/tools/loaders/__init__.py similarity index 100% rename from pkg/provider/tools/loaders/__init__.py rename to src/langbot/pkg/provider/tools/loaders/__init__.py diff --git a/pkg/provider/tools/loaders/mcp.py b/src/langbot/pkg/provider/tools/loaders/mcp.py similarity index 100% rename from pkg/provider/tools/loaders/mcp.py rename to src/langbot/pkg/provider/tools/loaders/mcp.py diff --git a/pkg/provider/tools/loaders/plugin.py b/src/langbot/pkg/provider/tools/loaders/plugin.py similarity index 100% rename from pkg/provider/tools/loaders/plugin.py rename to src/langbot/pkg/provider/tools/loaders/plugin.py diff --git a/pkg/provider/tools/toolmgr.py b/src/langbot/pkg/provider/tools/toolmgr.py similarity index 90% rename from pkg/provider/tools/toolmgr.py rename to src/langbot/pkg/provider/tools/toolmgr.py index a9379e80..f6b18a89 100644 --- a/pkg/provider/tools/toolmgr.py +++ b/src/langbot/pkg/provider/tools/toolmgr.py @@ -3,9 +3,9 @@ from __future__ import annotations import typing from ...core import app -from ...utils import importutil -from . import loaders -from .loaders import mcp as mcp_loader, plugin as plugin_loader +from langbot.pkg.utils import importutil +from langbot.pkg.provider.tools import loaders +from langbot.pkg.provider.tools.loaders import mcp as mcp_loader, plugin as plugin_loader import langbot_plugin.api.entities.builtin.resource.tool as resource_tool importutil.import_modules_in_pkg(loaders) @@ -28,7 +28,9 @@ class ToolManager: self.mcp_tool_loader = mcp_loader.MCPLoader(self.ap) await self.mcp_tool_loader.initialize() - async def get_all_tools(self, bound_plugins: list[str] | None = None, bound_mcp_servers: list[str] | None = None) -> list[resource_tool.LLMTool]: + async def get_all_tools( + self, bound_plugins: list[str] | None = None, bound_mcp_servers: list[str] | None = None + ) -> list[resource_tool.LLMTool]: """获取所有函数""" all_functions: list[resource_tool.LLMTool] = [] diff --git a/pkg/rag/knowledge/kbmgr.py b/src/langbot/pkg/rag/knowledge/kbmgr.py similarity index 96% rename from pkg/rag/knowledge/kbmgr.py rename to src/langbot/pkg/rag/knowledge/kbmgr.py index ed242696..17e2af32 100644 --- a/pkg/rag/knowledge/kbmgr.py +++ b/src/langbot/pkg/rag/knowledge/kbmgr.py @@ -4,13 +4,13 @@ import uuid import zipfile import io from .services import parser, chunker -from pkg.core import app -from pkg.rag.knowledge.services.embedder import Embedder -from pkg.rag.knowledge.services.retriever import Retriever +from langbot.pkg.core import app +from langbot.pkg.rag.knowledge.services.embedder import Embedder +from langbot.pkg.rag.knowledge.services.retriever import Retriever import sqlalchemy -from ...entity.persistence import rag as persistence_rag -from pkg.core import taskmgr -from ...entity.rag import retriever as retriever_entities +from langbot.pkg.entity.persistence import rag as persistence_rag +from langbot.pkg.core import taskmgr +from langbot.pkg.entity.rag import retriever as retriever_entities class RuntimeKnowledgeBase: diff --git a/pkg/rag/knowledge/services/__init__.py b/src/langbot/pkg/rag/knowledge/services/__init__.py similarity index 100% rename from pkg/rag/knowledge/services/__init__.py rename to src/langbot/pkg/rag/knowledge/services/__init__.py diff --git a/pkg/rag/knowledge/services/base_service.py b/src/langbot/pkg/rag/knowledge/services/base_service.py similarity index 100% rename from pkg/rag/knowledge/services/base_service.py rename to src/langbot/pkg/rag/knowledge/services/base_service.py diff --git a/pkg/rag/knowledge/services/chunker.py b/src/langbot/pkg/rag/knowledge/services/chunker.py similarity index 94% rename from pkg/rag/knowledge/services/chunker.py rename to src/langbot/pkg/rag/knowledge/services/chunker.py index 19b1f296..0cb16816 100644 --- a/pkg/rag/knowledge/services/chunker.py +++ b/src/langbot/pkg/rag/knowledge/services/chunker.py @@ -2,8 +2,8 @@ from __future__ import annotations import json from typing import List -from pkg.rag.knowledge.services import base_service -from pkg.core import app +from langbot.pkg.rag.knowledge.services import base_service +from langbot.pkg.core import app from langchain_text_splitters import RecursiveCharacterTextSplitter diff --git a/pkg/rag/knowledge/services/embedder.py b/src/langbot/pkg/rag/knowledge/services/embedder.py similarity index 86% rename from pkg/rag/knowledge/services/embedder.py rename to src/langbot/pkg/rag/knowledge/services/embedder.py index a0ae3d49..c8a1c3d3 100644 --- a/pkg/rag/knowledge/services/embedder.py +++ b/src/langbot/pkg/rag/knowledge/services/embedder.py @@ -1,10 +1,10 @@ from __future__ import annotations import uuid from typing import List -from pkg.rag.knowledge.services.base_service import BaseService -from ....entity.persistence import rag as persistence_rag -from ....core import app -from ....provider.modelmgr.requester import RuntimeEmbeddingModel +from langbot.pkg.rag.knowledge.services.base_service import BaseService +from langbot.pkg.entity.persistence import rag as persistence_rag +from langbot.pkg.core import app +from langbot.pkg.provider.modelmgr.requester import RuntimeEmbeddingModel import sqlalchemy diff --git a/pkg/rag/knowledge/services/parser.py b/src/langbot/pkg/rag/knowledge/services/parser.py similarity index 99% rename from pkg/rag/knowledge/services/parser.py rename to src/langbot/pkg/rag/knowledge/services/parser.py index 004dbdaa..50410738 100644 --- a/pkg/rag/knowledge/services/parser.py +++ b/src/langbot/pkg/rag/knowledge/services/parser.py @@ -9,7 +9,7 @@ import markdown from bs4 import BeautifulSoup import re import asyncio # Import asyncio for async operations -from pkg.core import app +from langbot.pkg.core import app class FileParser: diff --git a/pkg/rag/knowledge/services/retriever.py b/src/langbot/pkg/rag/knowledge/services/retriever.py similarity index 100% rename from pkg/rag/knowledge/services/retriever.py rename to src/langbot/pkg/rag/knowledge/services/retriever.py diff --git a/pkg/storage/__init__.py b/src/langbot/pkg/storage/__init__.py similarity index 100% rename from pkg/storage/__init__.py rename to src/langbot/pkg/storage/__init__.py diff --git a/pkg/storage/mgr.py b/src/langbot/pkg/storage/mgr.py similarity index 100% rename from pkg/storage/mgr.py rename to src/langbot/pkg/storage/mgr.py diff --git a/pkg/storage/provider.py b/src/langbot/pkg/storage/provider.py similarity index 100% rename from pkg/storage/provider.py rename to src/langbot/pkg/storage/provider.py diff --git a/pkg/storage/providers/__init__.py b/src/langbot/pkg/storage/providers/__init__.py similarity index 100% rename from pkg/storage/providers/__init__.py rename to src/langbot/pkg/storage/providers/__init__.py diff --git a/pkg/storage/providers/localstorage.py b/src/langbot/pkg/storage/providers/localstorage.py similarity index 100% rename from pkg/storage/providers/localstorage.py rename to src/langbot/pkg/storage/providers/localstorage.py diff --git a/pkg/storage/providers/s3storage.py b/src/langbot/pkg/storage/providers/s3storage.py similarity index 100% rename from pkg/storage/providers/s3storage.py rename to src/langbot/pkg/storage/providers/s3storage.py diff --git a/pkg/utils/__init__.py b/src/langbot/pkg/utils/__init__.py similarity index 100% rename from pkg/utils/__init__.py rename to src/langbot/pkg/utils/__init__.py diff --git a/pkg/utils/constants.py b/src/langbot/pkg/utils/constants.py similarity index 83% rename from pkg/utils/constants.py rename to src/langbot/pkg/utils/constants.py index c225ed82..503974af 100644 --- a/pkg/utils/constants.py +++ b/src/langbot/pkg/utils/constants.py @@ -1,4 +1,4 @@ -semantic_version = 'v4.5.0' +semantic_version = 'v4.6.0-beta.2' required_database_version = 11 """Tag the version of the database schema, used to check if the database needs to be migrated""" diff --git a/pkg/utils/funcschema.py b/src/langbot/pkg/utils/funcschema.py similarity index 100% rename from pkg/utils/funcschema.py rename to src/langbot/pkg/utils/funcschema.py diff --git a/pkg/utils/image.py b/src/langbot/pkg/utils/image.py similarity index 100% rename from pkg/utils/image.py rename to src/langbot/pkg/utils/image.py diff --git a/pkg/utils/importutil.py b/src/langbot/pkg/utils/importutil.py similarity index 58% rename from pkg/utils/importutil.py rename to src/langbot/pkg/utils/importutil.py index 1933d611..a35052a6 100644 --- a/pkg/utils/importutil.py +++ b/src/langbot/pkg/utils/importutil.py @@ -1,5 +1,5 @@ import importlib -import importlib.util +import importlib.resources import os import typing @@ -25,7 +25,7 @@ def import_dot_style_dir(dot_sep_path: str): return import_dir(os.path.join(*sec)) -def import_dir(path: str): +def import_dir(path: str, path_prefix: str = 'langbot.'): for file in os.listdir(path): if file.endswith('.py') and file != '__init__.py': full_path = os.path.join(path, file) @@ -33,10 +33,17 @@ def import_dir(path: str): rel_path = rel_path[1:] rel_path = rel_path.replace('/', '.')[:-3] rel_path = rel_path.replace('\\', '.') - importlib.import_module(rel_path) + importlib.import_module(f'{path_prefix}{rel_path}') -if __name__ == '__main__': - from pkg.platform import types +def read_resource_file(resource_path: str) -> str: + with importlib.resources.files('langbot').joinpath(resource_path).open('r', encoding='utf-8') as f: + return f.read() - import_modules_in_pkg(types) + +def read_resource_file_bytes(resource_path: str) -> bytes: + return importlib.resources.files('langbot').joinpath(resource_path).read_bytes() + + +def list_resource_files(resource_path: str) -> list[str]: + return [f.name for f in importlib.resources.files('langbot').joinpath(resource_path).iterdir()] diff --git a/pkg/utils/logcache.py b/src/langbot/pkg/utils/logcache.py similarity index 100% rename from pkg/utils/logcache.py rename to src/langbot/pkg/utils/logcache.py diff --git a/src/langbot/pkg/utils/paths.py b/src/langbot/pkg/utils/paths.py new file mode 100644 index 00000000..5553c154 --- /dev/null +++ b/src/langbot/pkg/utils/paths.py @@ -0,0 +1,92 @@ +"""Utility functions for finding package resources""" + +import os +from pathlib import Path + + +_is_source_install = None + + +def _check_if_source_install() -> bool: + """ + Check if we're running from source directory or an installed package. + Cached to avoid repeated file I/O. + """ + global _is_source_install + + if _is_source_install is not None: + return _is_source_install + + # Check if main.py exists in current directory with LangBot marker + if os.path.exists('main.py'): + try: + with open('main.py', 'r', encoding='utf-8') as f: + # Only read first 500 chars to check for marker + content = f.read(500) + if 'LangBot/main.py' in content: + _is_source_install = True + return True + except (IOError, OSError, UnicodeDecodeError): + # If we can't read the file, assume not a source install + pass + + _is_source_install = False + return False + + +def get_frontend_path() -> str: + """ + Get the path to the frontend build files. + + Returns the path to web/out directory, handling both: + - Development mode: running from source directory + - Package mode: installed via pip/uvx + """ + # First, check if we're running from source directory + if _check_if_source_install() and os.path.exists('web/out'): + return 'web/out' + + # Second, check current directory for web/out (in case user is in source dir) + if os.path.exists('web/out'): + return 'web/out' + + # Third, find it relative to the package installation + # Get the directory where this file is located + # paths.py is in pkg/utils/, so parent.parent goes up to pkg/, then parent again goes up to the package root + pkg_dir = Path(__file__).parent.parent.parent + frontend_path = pkg_dir / 'web' / 'out' + if frontend_path.exists(): + return str(frontend_path) + + # Return the default path (will be checked by caller) + return 'web/out' + + +def get_resource_path(resource: str) -> str: + """ + Get the path to a resource file. + + Args: + resource: Relative path to resource (e.g., 'templates/config.yaml') + + Returns: + Absolute path to the resource + """ + # First, check if resource exists in current directory (source install) + if _check_if_source_install() and os.path.exists(resource): + return resource + + # Second, check current directory anyway + if os.path.exists(resource): + return resource + + # Third, find it relative to package directory + # Get the directory where this file is located + # paths.py is in pkg/utils/, so parent.parent goes up to pkg/, then parent again goes up to the package root + pkg_dir = Path(__file__).parent.parent.parent + resource_path = pkg_dir / resource + if resource_path.exists(): + return str(resource_path) + + # Return the original path + return resource diff --git a/pkg/utils/pkgmgr.py b/src/langbot/pkg/utils/pkgmgr.py similarity index 100% rename from pkg/utils/pkgmgr.py rename to src/langbot/pkg/utils/pkgmgr.py diff --git a/pkg/utils/platform.py b/src/langbot/pkg/utils/platform.py similarity index 100% rename from pkg/utils/platform.py rename to src/langbot/pkg/utils/platform.py diff --git a/pkg/utils/proxy.py b/src/langbot/pkg/utils/proxy.py similarity index 100% rename from pkg/utils/proxy.py rename to src/langbot/pkg/utils/proxy.py diff --git a/pkg/utils/version.py b/src/langbot/pkg/utils/version.py similarity index 98% rename from pkg/utils/version.py rename to src/langbot/pkg/utils/version.py index 3a2748fc..60df3f32 100644 --- a/pkg/utils/version.py +++ b/src/langbot/pkg/utils/version.py @@ -38,7 +38,7 @@ class VersionManager: rls_list = rls_list_resp.json() return rls_list except Exception as e: - self.ap.logger.warning(f"获取发行列表失败: {e}") + self.ap.logger.warning(f'获取发行列表失败: {e}') pass return [] diff --git a/pkg/vector/__init__.py b/src/langbot/pkg/vector/__init__.py similarity index 100% rename from pkg/vector/__init__.py rename to src/langbot/pkg/vector/__init__.py diff --git a/pkg/vector/mgr.py b/src/langbot/pkg/vector/mgr.py similarity index 100% rename from pkg/vector/mgr.py rename to src/langbot/pkg/vector/mgr.py diff --git a/pkg/vector/vdb.py b/src/langbot/pkg/vector/vdb.py similarity index 100% rename from pkg/vector/vdb.py rename to src/langbot/pkg/vector/vdb.py diff --git a/pkg/vector/vdbs/__init__.py b/src/langbot/pkg/vector/vdbs/__init__.py similarity index 100% rename from pkg/vector/vdbs/__init__.py rename to src/langbot/pkg/vector/vdbs/__init__.py diff --git a/pkg/vector/vdbs/chroma.py b/src/langbot/pkg/vector/vdbs/chroma.py similarity index 96% rename from pkg/vector/vdbs/chroma.py rename to src/langbot/pkg/vector/vdbs/chroma.py index 41ab7d36..94227c75 100644 --- a/pkg/vector/vdbs/chroma.py +++ b/src/langbot/pkg/vector/vdbs/chroma.py @@ -2,8 +2,8 @@ from __future__ import annotations import asyncio from typing import Any from chromadb import PersistentClient -from pkg.vector.vdb import VectorDatabase -from pkg.core import app +from langbot.pkg.vector.vdb import VectorDatabase +from langbot.pkg.core import app import chromadb import chromadb.errors diff --git a/pkg/vector/vdbs/qdrant.py b/src/langbot/pkg/vector/vdbs/qdrant.py similarity index 98% rename from pkg/vector/vdbs/qdrant.py rename to src/langbot/pkg/vector/vdbs/qdrant.py index 85a1ad81..a6fbd4ab 100644 --- a/pkg/vector/vdbs/qdrant.py +++ b/src/langbot/pkg/vector/vdbs/qdrant.py @@ -3,8 +3,8 @@ from __future__ import annotations from typing import Any, Dict, List from qdrant_client import AsyncQdrantClient, models -from pkg.core import app -from pkg.vector.vdb import VectorDatabase +from langbot.pkg.core import app +from langbot.pkg.vector.vdb import VectorDatabase class QdrantVectorDatabase(VectorDatabase): diff --git a/templates/__init__.py b/src/langbot/templates/__init__.py similarity index 100% rename from templates/__init__.py rename to src/langbot/templates/__init__.py diff --git a/components.yaml b/src/langbot/templates/components.yaml similarity index 78% rename from components.yaml rename to src/langbot/templates/components.yaml index 03b2d04e..a95235aa 100644 --- a/components.yaml +++ b/src/langbot/templates/components.yaml @@ -7,9 +7,6 @@ metadata: zh_Hans: 内置组件 spec: components: - ComponentTemplate: - fromFiles: - - pkg/provider/modelmgr/requester.yaml MessagePlatformAdapter: fromDirs: - path: pkg/platform/sources/ diff --git a/templates/config.yaml b/src/langbot/templates/config.yaml similarity index 100% rename from templates/config.yaml rename to src/langbot/templates/config.yaml diff --git a/templates/default-pipeline-config.json b/src/langbot/templates/default-pipeline-config.json similarity index 100% rename from templates/default-pipeline-config.json rename to src/langbot/templates/default-pipeline-config.json diff --git a/templates/legacy/command.json b/src/langbot/templates/legacy/command.json similarity index 100% rename from templates/legacy/command.json rename to src/langbot/templates/legacy/command.json diff --git a/templates/legacy/pipeline.json b/src/langbot/templates/legacy/pipeline.json similarity index 100% rename from templates/legacy/pipeline.json rename to src/langbot/templates/legacy/pipeline.json diff --git a/templates/legacy/platform.json b/src/langbot/templates/legacy/platform.json similarity index 100% rename from templates/legacy/platform.json rename to src/langbot/templates/legacy/platform.json diff --git a/templates/legacy/provider.json b/src/langbot/templates/legacy/provider.json similarity index 100% rename from templates/legacy/provider.json rename to src/langbot/templates/legacy/provider.json diff --git a/templates/legacy/system.json b/src/langbot/templates/legacy/system.json similarity index 100% rename from templates/legacy/system.json rename to src/langbot/templates/legacy/system.json diff --git a/templates/metadata/pipeline/ai.yaml b/src/langbot/templates/metadata/pipeline/ai.yaml similarity index 100% rename from templates/metadata/pipeline/ai.yaml rename to src/langbot/templates/metadata/pipeline/ai.yaml diff --git a/templates/metadata/pipeline/output.yaml b/src/langbot/templates/metadata/pipeline/output.yaml similarity index 100% rename from templates/metadata/pipeline/output.yaml rename to src/langbot/templates/metadata/pipeline/output.yaml diff --git a/templates/metadata/pipeline/safety.yaml b/src/langbot/templates/metadata/pipeline/safety.yaml similarity index 100% rename from templates/metadata/pipeline/safety.yaml rename to src/langbot/templates/metadata/pipeline/safety.yaml diff --git a/templates/metadata/pipeline/trigger.yaml b/src/langbot/templates/metadata/pipeline/trigger.yaml similarity index 100% rename from templates/metadata/pipeline/trigger.yaml rename to src/langbot/templates/metadata/pipeline/trigger.yaml diff --git a/templates/metadata/sensitive-words.json b/src/langbot/templates/metadata/sensitive-words.json similarity index 100% rename from templates/metadata/sensitive-words.json rename to src/langbot/templates/metadata/sensitive-words.json diff --git a/tests/unit_tests/pipeline/conftest.py b/tests/unit_tests/pipeline/conftest.py index f6935395..40b2e930 100644 --- a/tests/unit_tests/pipeline/conftest.py +++ b/tests/unit_tests/pipeline/conftest.py @@ -10,16 +10,13 @@ This file provides infrastructure for all pipeline tests, including: from __future__ import annotations import pytest -from unittest.mock import AsyncMock, MagicMock, Mock -from typing import Any +from unittest.mock import AsyncMock, Mock import langbot_plugin.api.entities.builtin.pipeline.query as pipeline_query import langbot_plugin.api.entities.builtin.platform.message as platform_message -import langbot_plugin.api.entities.builtin.platform.events as platform_events import langbot_plugin.api.entities.builtin.provider.session as provider_session -import langbot_plugin.api.entities.builtin.provider.message as provider_message -from pkg.pipeline import entities as pipeline_entities +from langbot.pkg.pipeline import entities as pipeline_entities class MockApplication: @@ -203,7 +200,7 @@ def sample_query(sample_message_chain, sample_message_event, mock_adapter): variables={}, resp_messages=[], resp_message_chain=None, - current_stage_name=None + current_stage_name=None, ) return query diff --git a/tests/unit_tests/pipeline/test_bansess.py b/tests/unit_tests/pipeline/test_bansess.py index 2483d484..394fb8bc 100644 --- a/tests/unit_tests/pipeline/test_bansess.py +++ b/tests/unit_tests/pipeline/test_bansess.py @@ -5,7 +5,6 @@ Tests the actual BanSessionCheckStage implementation from pkg.pipeline.bansess """ import pytest -from unittest.mock import Mock from importlib import import_module import langbot_plugin.api.entities.builtin.provider.session as provider_session @@ -13,9 +12,8 @@ import langbot_plugin.api.entities.builtin.provider.session as provider_session def get_modules(): """Lazy import to ensure proper initialization order""" # Import pipelinemgr first to trigger proper stage registration - pipelinemgr = import_module('pkg.pipeline.pipelinemgr') - bansess = import_module('pkg.pipeline.bansess.bansess') - entities = import_module('pkg.pipeline.entities') + bansess = import_module('langbot.pkg.pipeline.bansess.bansess') + entities = import_module('langbot.pkg.pipeline.entities') return bansess, entities @@ -26,14 +24,7 @@ async def test_whitelist_allow(mock_app, sample_query): sample_query.launcher_type = provider_session.LauncherTypes.PERSON sample_query.launcher_id = '12345' - sample_query.pipeline_config = { - 'trigger': { - 'access-control': { - 'mode': 'whitelist', - 'whitelist': ['person_12345'] - } - } - } + sample_query.pipeline_config = {'trigger': {'access-control': {'mode': 'whitelist', 'whitelist': ['person_12345']}}} stage = bansess.BanSessionCheckStage(mock_app) await stage.initialize(sample_query.pipeline_config) @@ -51,14 +42,7 @@ async def test_whitelist_deny(mock_app, sample_query): sample_query.launcher_type = provider_session.LauncherTypes.PERSON sample_query.launcher_id = '99999' - sample_query.pipeline_config = { - 'trigger': { - 'access-control': { - 'mode': 'whitelist', - 'whitelist': ['person_12345'] - } - } - } + sample_query.pipeline_config = {'trigger': {'access-control': {'mode': 'whitelist', 'whitelist': ['person_12345']}}} stage = bansess.BanSessionCheckStage(mock_app) await stage.initialize(sample_query.pipeline_config) @@ -75,14 +59,7 @@ async def test_blacklist_allow(mock_app, sample_query): sample_query.launcher_type = provider_session.LauncherTypes.PERSON sample_query.launcher_id = '12345' - sample_query.pipeline_config = { - 'trigger': { - 'access-control': { - 'mode': 'blacklist', - 'blacklist': ['person_99999'] - } - } - } + sample_query.pipeline_config = {'trigger': {'access-control': {'mode': 'blacklist', 'blacklist': ['person_99999']}}} stage = bansess.BanSessionCheckStage(mock_app) await stage.initialize(sample_query.pipeline_config) @@ -99,14 +76,7 @@ async def test_blacklist_deny(mock_app, sample_query): sample_query.launcher_type = provider_session.LauncherTypes.PERSON sample_query.launcher_id = '12345' - sample_query.pipeline_config = { - 'trigger': { - 'access-control': { - 'mode': 'blacklist', - 'blacklist': ['person_12345'] - } - } - } + sample_query.pipeline_config = {'trigger': {'access-control': {'mode': 'blacklist', 'blacklist': ['person_12345']}}} stage = bansess.BanSessionCheckStage(mock_app) await stage.initialize(sample_query.pipeline_config) @@ -123,14 +93,7 @@ async def test_wildcard_group(mock_app, sample_query): sample_query.launcher_type = provider_session.LauncherTypes.GROUP sample_query.launcher_id = '12345' - sample_query.pipeline_config = { - 'trigger': { - 'access-control': { - 'mode': 'whitelist', - 'whitelist': ['group_*'] - } - } - } + sample_query.pipeline_config = {'trigger': {'access-control': {'mode': 'whitelist', 'whitelist': ['group_*']}}} stage = bansess.BanSessionCheckStage(mock_app) await stage.initialize(sample_query.pipeline_config) @@ -147,14 +110,7 @@ async def test_wildcard_person(mock_app, sample_query): sample_query.launcher_type = provider_session.LauncherTypes.PERSON sample_query.launcher_id = '12345' - sample_query.pipeline_config = { - 'trigger': { - 'access-control': { - 'mode': 'whitelist', - 'whitelist': ['person_*'] - } - } - } + sample_query.pipeline_config = {'trigger': {'access-control': {'mode': 'whitelist', 'whitelist': ['person_*']}}} stage = bansess.BanSessionCheckStage(mock_app) await stage.initialize(sample_query.pipeline_config) @@ -172,14 +128,7 @@ async def test_user_id_wildcard(mock_app, sample_query): sample_query.launcher_type = provider_session.LauncherTypes.PERSON sample_query.launcher_id = '12345' sample_query.sender_id = '67890' - sample_query.pipeline_config = { - 'trigger': { - 'access-control': { - 'mode': 'whitelist', - 'whitelist': ['*_67890'] - } - } - } + sample_query.pipeline_config = {'trigger': {'access-control': {'mode': 'whitelist', 'whitelist': ['*_67890']}}} stage = bansess.BanSessionCheckStage(mock_app) await stage.initialize(sample_query.pipeline_config) diff --git a/tests/unit_tests/pipeline/test_pipelinemgr.py b/tests/unit_tests/pipeline/test_pipelinemgr.py index c500c1c1..95c6d968 100644 --- a/tests/unit_tests/pipeline/test_pipelinemgr.py +++ b/tests/unit_tests/pipeline/test_pipelinemgr.py @@ -8,19 +8,19 @@ from importlib import import_module def get_pipelinemgr_module(): - return import_module('pkg.pipeline.pipelinemgr') + return import_module('langbot.pkg.pipeline.pipelinemgr') def get_stage_module(): - return import_module('pkg.pipeline.stage') + return import_module('langbot.pkg.pipeline.stage') def get_entities_module(): - return import_module('pkg.pipeline.entities') + return import_module('langbot.pkg.pipeline.entities') def get_persistence_pipeline_module(): - return import_module('pkg.entity.persistence.pipeline') + return import_module('langbot.pkg.entity.persistence.pipeline') @pytest.mark.asyncio diff --git a/tests/unit_tests/pipeline/test_ratelimit.py b/tests/unit_tests/pipeline/test_ratelimit.py index 18e399fe..77649f70 100644 --- a/tests/unit_tests/pipeline/test_ratelimit.py +++ b/tests/unit_tests/pipeline/test_ratelimit.py @@ -13,10 +13,9 @@ import langbot_plugin.api.entities.builtin.provider.session as provider_session def get_modules(): """Lazy import to ensure proper initialization order""" # Import pipelinemgr first to trigger proper stage registration - pipelinemgr = import_module('pkg.pipeline.pipelinemgr') - ratelimit = import_module('pkg.pipeline.ratelimit.ratelimit') - entities = import_module('pkg.pipeline.entities') - algo_module = import_module('pkg.pipeline.ratelimit.algo') + ratelimit = import_module('langbot.pkg.pipeline.ratelimit.ratelimit') + entities = import_module('langbot.pkg.pipeline.entities') + algo_module = import_module('langbot.pkg.pipeline.ratelimit.algo') return ratelimit, entities, algo_module @@ -44,11 +43,7 @@ async def test_require_access_allowed(mock_app, sample_query): assert result.result_type == entities.ResultType.CONTINUE assert result.new_query == sample_query - mock_algo.require_access.assert_called_once_with( - sample_query, - 'person', - '12345' - ) + mock_algo.require_access.assert_called_once_with(sample_query, 'person', '12345') @pytest.mark.asyncio @@ -102,8 +97,4 @@ async def test_release_access(mock_app, sample_query): assert result.result_type == entities.ResultType.CONTINUE assert result.new_query == sample_query - mock_algo.release_access.assert_called_once_with( - sample_query, - 'person', - '12345' - ) + mock_algo.release_access.assert_called_once_with(sample_query, 'person', '12345') diff --git a/tests/unit_tests/pipeline/test_resprule.py b/tests/unit_tests/pipeline/test_resprule.py index 69df165b..63dfbfd0 100644 --- a/tests/unit_tests/pipeline/test_resprule.py +++ b/tests/unit_tests/pipeline/test_resprule.py @@ -14,11 +14,11 @@ import langbot_plugin.api.entities.builtin.platform.message as platform_message def get_modules(): """Lazy import to ensure proper initialization order""" # Import pipelinemgr first to trigger proper stage registration - pipelinemgr = import_module('pkg.pipeline.pipelinemgr') - resprule = import_module('pkg.pipeline.resprule.resprule') - entities = import_module('pkg.pipeline.entities') - rule = import_module('pkg.pipeline.resprule.rule') - rule_entities = import_module('pkg.pipeline.resprule.entities') + # pipelinemgr = import_module('langbot.pkg.pipeline.pipelinemgr') + resprule = import_module('langbot.pkg.pipeline.resprule.resprule') + entities = import_module('langbot.pkg.pipeline.entities') + rule = import_module('langbot.pkg.pipeline.resprule.rule') + rule_entities = import_module('langbot.pkg.pipeline.resprule.entities') return resprule, entities, rule, rule_entities @@ -28,11 +28,7 @@ async def test_person_message_skip(mock_app, sample_query): resprule, entities, rule, rule_entities = get_modules() sample_query.launcher_type = provider_session.LauncherTypes.PERSON - sample_query.pipeline_config = { - 'trigger': { - 'group-respond-rules': {} - } - } + sample_query.pipeline_config = {'trigger': {'group-respond-rules': {}}} stage = resprule.GroupRespondRuleCheckStage(mock_app) await stage.initialize(sample_query.pipeline_config) @@ -50,18 +46,13 @@ async def test_group_message_no_match(mock_app, sample_query): sample_query.launcher_type = provider_session.LauncherTypes.GROUP sample_query.launcher_id = '12345' - sample_query.pipeline_config = { - 'trigger': { - 'group-respond-rules': {} - } - } + sample_query.pipeline_config = {'trigger': {'group-respond-rules': {}}} # Create mock rule matcher that doesn't match mock_rule = Mock(spec=rule.GroupRespondRule) - mock_rule.match = AsyncMock(return_value=rule_entities.RuleJudgeResult( - matching=False, - replacement=sample_query.message_chain - )) + mock_rule.match = AsyncMock( + return_value=rule_entities.RuleJudgeResult(matching=False, replacement=sample_query.message_chain) + ) stage = resprule.GroupRespondRuleCheckStage(mock_app) await stage.initialize(sample_query.pipeline_config) @@ -81,23 +72,14 @@ async def test_group_message_match(mock_app, sample_query): sample_query.launcher_type = provider_session.LauncherTypes.GROUP sample_query.launcher_id = '12345' - sample_query.pipeline_config = { - 'trigger': { - 'group-respond-rules': {} - } - } + sample_query.pipeline_config = {'trigger': {'group-respond-rules': {}}} # Create new message chain after rule processing - new_chain = platform_message.MessageChain([ - platform_message.Plain(text='Processed message') - ]) + new_chain = platform_message.MessageChain([platform_message.Plain(text='Processed message')]) # Create mock rule matcher that matches mock_rule = Mock(spec=rule.GroupRespondRule) - mock_rule.match = AsyncMock(return_value=rule_entities.RuleJudgeResult( - matching=True, - replacement=new_chain - )) + mock_rule.match = AsyncMock(return_value=rule_entities.RuleJudgeResult(matching=True, replacement=new_chain)) stage = resprule.GroupRespondRuleCheckStage(mock_app) await stage.initialize(sample_query.pipeline_config) @@ -115,27 +97,21 @@ async def test_group_message_match(mock_app, sample_query): async def test_atbot_rule_match(mock_app, sample_query): """Test AtBotRule removes At component""" resprule, entities, rule, rule_entities = get_modules() - atbot_module = import_module('pkg.pipeline.resprule.rules.atbot') + atbot_module = import_module('langbot.pkg.pipeline.resprule.rules.atbot') sample_query.launcher_type = provider_session.LauncherTypes.GROUP sample_query.adapter.bot_account_id = '999' # Create message chain with At component - message_chain = platform_message.MessageChain([ - platform_message.At(target='999'), - platform_message.Plain(text='Hello bot') - ]) + message_chain = platform_message.MessageChain( + [platform_message.At(target='999'), platform_message.Plain(text='Hello bot')] + ) sample_query.message_chain = message_chain atbot_rule = atbot_module.AtBotRule(mock_app) await atbot_rule.initialize() - result = await atbot_rule.match( - str(message_chain), - message_chain, - {}, - sample_query - ) + result = await atbot_rule.match(str(message_chain), message_chain, {}, sample_query) assert result.matching is True # At component should be removed @@ -147,25 +123,18 @@ async def test_atbot_rule_match(mock_app, sample_query): async def test_atbot_rule_no_match(mock_app, sample_query): """Test AtBotRule when no At component present""" resprule, entities, rule, rule_entities = get_modules() - atbot_module = import_module('pkg.pipeline.resprule.rules.atbot') + atbot_module = import_module('langbot.pkg.pipeline.resprule.rules.atbot') sample_query.launcher_type = provider_session.LauncherTypes.GROUP sample_query.adapter.bot_account_id = '999' # Create message chain without At component - message_chain = platform_message.MessageChain([ - platform_message.Plain(text='Hello') - ]) + message_chain = platform_message.MessageChain([platform_message.Plain(text='Hello')]) sample_query.message_chain = message_chain atbot_rule = atbot_module.AtBotRule(mock_app) await atbot_rule.initialize() - result = await atbot_rule.match( - str(message_chain), - message_chain, - {}, - sample_query - ) + result = await atbot_rule.match(str(message_chain), message_chain, {}, sample_query) assert result.matching is False diff --git a/tests/unit_tests/storage/test_storage_provider_selection.py b/tests/unit_tests/storage/test_storage_provider_selection.py index 9f87f10a..c5811e3c 100644 --- a/tests/unit_tests/storage/test_storage_provider_selection.py +++ b/tests/unit_tests/storage/test_storage_provider_selection.py @@ -4,9 +4,9 @@ Tests for storage manager and provider selection import pytest from unittest.mock import Mock, AsyncMock, patch -from pkg.storage.mgr import StorageMgr -from pkg.storage.providers.localstorage import LocalStorageProvider -from pkg.storage.providers.s3storage import S3StorageProvider +from langbot.pkg.storage.mgr import StorageMgr +from langbot.pkg.storage.providers.localstorage import LocalStorageProvider +from langbot.pkg.storage.providers.s3storage import S3StorageProvider class TestStorageProviderSelection: @@ -34,11 +34,7 @@ class TestStorageProviderSelection: # Mock application mock_app = Mock() mock_app.instance_config = Mock() - mock_app.instance_config.data = { - 'storage': { - 'use': 'local' - } - } + mock_app.instance_config.data = {'storage': {'use': 'local'}} mock_app.logger = Mock() storage_mgr = StorageMgr(mock_app) @@ -62,8 +58,8 @@ class TestStorageProviderSelection: 'access_key_id': 'test_key', 'secret_access_key': 'test_secret', 'region': 'us-east-1', - 'bucket': 'test-bucket' - } + 'bucket': 'test-bucket', + }, } } mock_app.logger = Mock() @@ -81,11 +77,7 @@ class TestStorageProviderSelection: # Mock application mock_app = Mock() mock_app.instance_config = Mock() - mock_app.instance_config.data = { - 'storage': { - 'use': 'invalid_type' - } - } + mock_app.instance_config.data = {'storage': {'use': 'invalid_type'}} mock_app.logger = Mock() storage_mgr = StorageMgr(mock_app)