langbot-plugin 0.5.1__tar.gz → 0.5.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/PKG-INFO +1 -1
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/pyproject.toml +1 -1
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/proxies/langbot_api.py +5 -6
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/run/handler.py +4 -4
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/entities/io/context.py +29 -4
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/context.py +55 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/helper/marketplace.py +2 -6
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/helper/pkgmgr.py +1 -4
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/connections/stdio.py +3 -10
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/connections/ws.py +3 -7
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/handler.py +22 -23
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/plugin/dependency_environment.py +16 -70
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/plugin/mgr.py +439 -236
- langbot_plugin-0.5.2/src/langbot_plugin/runtime/plugin/restart_coordinator.py +418 -0
- langbot_plugin-0.5.2/src/langbot_plugin/version.py +1 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/entities/io/test_protocol.py +89 -5
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/io/handlers/test_control_handler.py +6 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/plugin/test_installation_manager.py +559 -14
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/plugin/test_restart_coordinator.py +129 -5
- langbot_plugin-0.5.1/src/langbot_plugin/runtime/plugin/restart_coordinator.py +0 -293
- langbot_plugin-0.5.1/src/langbot_plugin/version.py +0 -1
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/.github/workflows/cla.yml +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/.github/workflows/publish-pypi.yaml +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/.github/workflows/publish.yml +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/.github/workflows/test.yml +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/.gitignore +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/.python-version +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/AGENTS.md +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/ARCHITECTURE.md +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/CLAUDE.md +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/CONTRIBUTING.md +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/LICENSE +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/README.md +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/README_CN.md +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/data/.env.example +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/docs/Message.md +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/docs/PluginPages.md +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/docs/communication/apis/lb2rt.md +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/docs/communication/runtime_plugin.md +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/docs/dependency-management.md +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/docs/langbot-plugin-social.png +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/scripts/check_action_consistency.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/scripts/runtime_resource_probe.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/abstract/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/abstract/platform/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/abstract/platform/adapter.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/abstract/platform/event_logger.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/base.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/command/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/command/command.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/common/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/common/event_listener.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/knowledge_engine/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/knowledge_engine/engine.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/manifest.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/page/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/parser/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/parser/parser.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/tool/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/components/tool/tool.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/definition/plugin.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/command/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/command/context.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/command/errors.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/pipeline/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/pipeline/query.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/platform/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/platform/entities.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/platform/events.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/platform/logger.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/platform/message.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/provider/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/provider/message.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/provider/prompt.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/provider/session.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/rag/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/rag/context.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/rag/enums.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/rag/errors.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/rag/models.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/resource/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/builtin/resource/tool.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/context.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/events.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/entities/execution.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/proxies/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/proxies/base.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/proxies/event_context.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/proxies/execute_context.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/api/proxies/query_based_api.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/langbot-page-sdk.js +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/.env.example.example +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/.github/workflows/release.yml.example +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/.gitignore.example +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/.vscode/launch.json.example +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/README.md.example +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/assets/icon.svg.example +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/commands/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/commands/{cmd_name}.py.example +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/commands/{cmd_name}.yaml.example +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/event_listener/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/event_listener/default.py.example +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/event_listener/default.yaml.example +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/knowledge_engine/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/knowledge_engine/{knowledge_engine_name}.py.example +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/knowledge_engine/{knowledge_engine_name}.yaml.example +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/pages/{page_name}.html.example +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/pages/{page_name}.yaml.example +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/parser/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/parser/{parser_name}.py.example +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/parser/{parser_name}.yaml.example +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/tools/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/tools/{tool_name}.py.example +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/components/tools/{tool_name}.yaml.example +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/main.py.example +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/manifest.yaml.example +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/readme/README_zh_Hans.md.example +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/assets/templates/requirements.txt.example +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/box/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/box/actions.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/box/backend.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/box/client.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/box/e2b_backend.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/box/errors.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/box/models.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/box/nsjail_backend.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/box/runtime.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/box/security.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/box/server.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/box/skill_store.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/box/tenancy.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/__main__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/commands/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/commands/buildplugin.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/commands/gencomponent.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/commands/initplugin.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/commands/login.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/commands/logout.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/commands/publish.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/commands/runplugin.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/gen/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/gen/renderer.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/i18n.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/locales/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/locales/en_US.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/locales/es_ES.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/locales/ja_JP.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/locales/th_TH.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/locales/vi_VN.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/locales/zh_Hans.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/locales/zh_Hant.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/run/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/run/controller.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/run/hotreload.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/utils/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/utils/cloudsv.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/utils/form.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/cli/utils/page_components.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/entities/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/entities/io/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/entities/io/actions/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/entities/io/actions/enums.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/entities/io/errors.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/entities/io/req.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/entities/io/resp.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/entities/marketplace.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/LICENSE +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/README.md +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/app.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/bounded_executor.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/event_loop_monitor.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/helper/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/connection.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/connections/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/controller.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/controllers/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/controllers/stdio/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/controllers/stdio/client.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/controllers/stdio/server.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/controllers/ws/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/controllers/ws/client.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/controllers/ws/server.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/handlers/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/handlers/control.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/handlers/plugin.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/plugin/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/plugin/artifact.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/plugin/container.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/plugin/logbuffer.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/plugin/worker_launcher.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/security.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/settings.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/utils/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/utils/discover/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/utils/discover/engine.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/utils/importutil.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/utils/log.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/utils/platform.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/api/definition/components/test_components.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/api/definition/test_manifest.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/api/entities/builtin/test_command_context.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/api/entities/builtin/test_platform_logger.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/api/entities/builtin/test_provider_message.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/api/entities/builtin/test_rag_models.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/api/entities/test_context.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/api/entities/test_events.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/api/proxies/test_base.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/api/proxies/test_langbot_api.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/api/proxies/test_query_based_api.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/box/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/box/test_admission.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/box/test_backend.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/box/test_backend_selection.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/box/test_client.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/box/test_e2b_backend.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/box/test_nsjail_backend.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/box/test_runtime.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/box/test_server.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/box/test_skill_store.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/cli/run/test_controller.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/cli/run/test_hotreload.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/cli/run/test_runtime_handler.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/cli/test_buildplugin.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/cli/test_gencomponent.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/cli/test_i18n_form.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/cli/test_initplugin.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/cli/test_login.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/cli/test_logout_publish.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/cli/test_page_components.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/cli/test_renderer.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/cli/test_runplugin.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/entities/io/test_dependency_errors.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/helpers/__init__.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/helpers/protocol.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/packaging/test_installed_cli_blackbox.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/helper/test_marketplace.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/helper/test_pkgmgr.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/io/handlers/test_import_contracts.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/io/handlers/test_plugin_handler.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/io/test_action_consistency.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/io/test_connections.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/io/test_controllers.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/io/test_handler.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/plugin/test_artifact_store.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/plugin/test_container.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/plugin/test_dependency_environment.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/plugin/test_manager.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/plugin/test_worker_launcher.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/test_app.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/test_bounded_executor.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/runtime/test_event_loop_monitor.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/test_log.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/test_message.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/test_plugin_logbuffer.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/utils/test_discovery.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/utils/test_importutil.py +0 -0
- {langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/tests/utils/test_platform.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: langbot-plugin
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.2
|
|
4
4
|
Summary: This package contains the SDK, CLI for building plugins for LangBot, plus the runtime for hosting LangBot plugins
|
|
5
5
|
Project-URL: Homepage, https://langbot.app
|
|
6
6
|
Project-URL: Repository, https://github.com/langbot-app/langbot-plugin-sdk
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import asyncio
|
|
4
3
|
import base64
|
|
5
4
|
from typing import Any
|
|
6
5
|
|
|
@@ -178,7 +177,7 @@ class LangBotAPIProxy:
|
|
|
178
177
|
|
|
179
178
|
async def set_plugin_storage(self, key: str, value: bytes) -> None:
|
|
180
179
|
"""Set a plugin storage value"""
|
|
181
|
-
encoded =
|
|
180
|
+
encoded = base64.b64encode(value).decode("utf-8")
|
|
182
181
|
await self.plugin_runtime_handler.call_action(
|
|
183
182
|
PluginToRuntimeAction.SET_PLUGIN_STORAGE,
|
|
184
183
|
{"key": key, "value_base64": encoded},
|
|
@@ -192,7 +191,7 @@ class LangBotAPIProxy:
|
|
|
192
191
|
)
|
|
193
192
|
)["value_base64"]
|
|
194
193
|
|
|
195
|
-
return
|
|
194
|
+
return base64.b64decode(resp)
|
|
196
195
|
|
|
197
196
|
async def get_plugin_storage_keys(self) -> list[str]:
|
|
198
197
|
"""Get all plugin storage keys"""
|
|
@@ -210,7 +209,7 @@ class LangBotAPIProxy:
|
|
|
210
209
|
|
|
211
210
|
async def set_workspace_storage(self, key: str, value: bytes) -> None:
|
|
212
211
|
"""Set a workspace storage value"""
|
|
213
|
-
encoded =
|
|
212
|
+
encoded = base64.b64encode(value).decode("utf-8")
|
|
214
213
|
await self.plugin_runtime_handler.call_action(
|
|
215
214
|
PluginToRuntimeAction.SET_WORKSPACE_STORAGE,
|
|
216
215
|
{"key": key, "value_base64": encoded},
|
|
@@ -224,7 +223,7 @@ class LangBotAPIProxy:
|
|
|
224
223
|
)
|
|
225
224
|
)["value_base64"]
|
|
226
225
|
|
|
227
|
-
return
|
|
226
|
+
return base64.b64decode(resp)
|
|
228
227
|
|
|
229
228
|
async def get_workspace_storage_keys(self) -> list[str]:
|
|
230
229
|
"""Get all workspace storage keys"""
|
|
@@ -255,7 +254,7 @@ class LangBotAPIProxy:
|
|
|
255
254
|
)
|
|
256
255
|
)["file_base64"]
|
|
257
256
|
|
|
258
|
-
return
|
|
257
|
+
return base64.b64decode(resp)
|
|
259
258
|
|
|
260
259
|
async def list_plugins_manifest(self) -> list[str]:
|
|
261
260
|
"""List all plugins"""
|
|
@@ -4,7 +4,6 @@ import asyncio
|
|
|
4
4
|
import os
|
|
5
5
|
import mimetypes
|
|
6
6
|
import typing
|
|
7
|
-
import aiofiles
|
|
8
7
|
import logging
|
|
9
8
|
from copy import deepcopy
|
|
10
9
|
from pathlib import Path
|
|
@@ -43,12 +42,13 @@ MAX_RUNTIME_UI_FILE_BYTES = 4 * 1024 * 1024
|
|
|
43
42
|
|
|
44
43
|
|
|
45
44
|
async def _read_runtime_ui_file_limited(path: str | Path) -> bytes:
|
|
46
|
-
|
|
45
|
+
file_path = Path(path)
|
|
46
|
+
if file_path.stat().st_size > MAX_RUNTIME_UI_FILE_BYTES:
|
|
47
47
|
raise ValueError(
|
|
48
48
|
f"Plugin UI file exceeds the {MAX_RUNTIME_UI_FILE_BYTES}-byte limit"
|
|
49
49
|
)
|
|
50
|
-
|
|
51
|
-
content =
|
|
50
|
+
with file_path.open("rb") as file:
|
|
51
|
+
content = file.read(MAX_RUNTIME_UI_FILE_BYTES + 1)
|
|
52
52
|
if len(content) > MAX_RUNTIME_UI_FILE_BYTES:
|
|
53
53
|
raise ValueError(
|
|
54
54
|
f"Plugin UI file exceeds the {MAX_RUNTIME_UI_FILE_BYTES}-byte limit"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import json
|
|
3
4
|
import math
|
|
4
5
|
import re
|
|
5
6
|
from typing import Literal
|
|
@@ -39,6 +40,7 @@ class PluginWorkerPolicy(pydantic.BaseModel):
|
|
|
39
40
|
max_total_cpus: float = 8.0
|
|
40
41
|
max_total_memory_mb: int = 8192
|
|
41
42
|
max_installations: int = 10_000
|
|
43
|
+
max_pending_registrations: int = 1024
|
|
42
44
|
max_concurrent_restarts: int = 1
|
|
43
45
|
restart_failure_threshold: int = 8
|
|
44
46
|
restart_failure_window_seconds: float = 30.0
|
|
@@ -62,6 +64,7 @@ class PluginWorkerPolicy(pydantic.BaseModel):
|
|
|
62
64
|
"max_workers",
|
|
63
65
|
"max_total_memory_mb",
|
|
64
66
|
"max_installations",
|
|
67
|
+
"max_pending_registrations",
|
|
65
68
|
"max_concurrent_restarts",
|
|
66
69
|
"restart_failure_threshold",
|
|
67
70
|
mode="before",
|
|
@@ -112,6 +115,7 @@ class PluginWorkerPolicy(pydantic.BaseModel):
|
|
|
112
115
|
"max_workers",
|
|
113
116
|
"max_total_memory_mb",
|
|
114
117
|
"max_installations",
|
|
118
|
+
"max_pending_registrations",
|
|
115
119
|
"max_concurrent_restarts",
|
|
116
120
|
"restart_failure_threshold",
|
|
117
121
|
)
|
|
@@ -129,10 +133,6 @@ class PluginWorkerPolicy(pydantic.BaseModel):
|
|
|
129
133
|
raise ValueError("max_total_memory_mb cannot be lower than max_memory_mb")
|
|
130
134
|
if self.max_installations < self.max_workers:
|
|
131
135
|
raise ValueError("max_installations cannot be lower than max_workers")
|
|
132
|
-
if self.max_concurrent_restarts > self.effective_worker_capacity:
|
|
133
|
-
raise ValueError(
|
|
134
|
-
"max_concurrent_restarts cannot exceed effective worker capacity"
|
|
135
|
-
)
|
|
136
136
|
if self.restart_failure_threshold > self.max_installations:
|
|
137
137
|
raise ValueError(
|
|
138
138
|
"restart_failure_threshold cannot exceed max_installations"
|
|
@@ -292,6 +292,31 @@ class RuntimeConfig(pydantic.BaseModel):
|
|
|
292
292
|
|
|
293
293
|
model_config = pydantic.ConfigDict(extra="forbid", frozen=True)
|
|
294
294
|
|
|
295
|
+
def model_dump(self, **kwargs):
|
|
296
|
+
include_pending_registration_limit = kwargs.pop(
|
|
297
|
+
"include_pending_registration_limit",
|
|
298
|
+
False,
|
|
299
|
+
)
|
|
300
|
+
payload = super().model_dump(**kwargs)
|
|
301
|
+
if not include_pending_registration_limit and isinstance(
|
|
302
|
+
payload.get("worker_policy"),
|
|
303
|
+
dict,
|
|
304
|
+
):
|
|
305
|
+
payload["worker_policy"].pop("max_pending_registrations", None)
|
|
306
|
+
return payload
|
|
307
|
+
|
|
308
|
+
def model_dump_json(self, **kwargs) -> str:
|
|
309
|
+
include_pending_registration_limit = kwargs.pop(
|
|
310
|
+
"include_pending_registration_limit",
|
|
311
|
+
False,
|
|
312
|
+
)
|
|
313
|
+
return json.dumps(
|
|
314
|
+
self.model_dump(
|
|
315
|
+
include_pending_registration_limit=include_pending_registration_limit,
|
|
316
|
+
**kwargs,
|
|
317
|
+
)
|
|
318
|
+
)
|
|
319
|
+
|
|
295
320
|
@pydantic.field_validator("cloud_service_url")
|
|
296
321
|
@classmethod
|
|
297
322
|
def validate_cloud_service_url(cls, value: str | None) -> str | None:
|
|
@@ -70,6 +70,17 @@ class RuntimeContext:
|
|
|
70
70
|
getattr(plugin_manager, "handlers", ()),
|
|
71
71
|
)
|
|
72
72
|
event_loop_monitor = self.event_loop_monitor
|
|
73
|
+
installation_state_counts = {
|
|
74
|
+
"running": 0,
|
|
75
|
+
"starting": 0,
|
|
76
|
+
"failed": 0,
|
|
77
|
+
"disabled": 0,
|
|
78
|
+
}
|
|
79
|
+
for runtime in getattr(plugin_manager, "_installations", {}).values():
|
|
80
|
+
state = str(getattr(runtime, "state", "disabled"))
|
|
81
|
+
if state in installation_state_counts:
|
|
82
|
+
installation_state_counts[state] += 1
|
|
83
|
+
|
|
73
84
|
return {
|
|
74
85
|
"event_loop": (
|
|
75
86
|
event_loop_monitor.snapshot() if event_loop_monitor is not None else {}
|
|
@@ -84,6 +95,7 @@ class RuntimeContext:
|
|
|
84
95
|
getattr(plugin_manager, "_plugin_supervisors", ())
|
|
85
96
|
),
|
|
86
97
|
"installation_runtimes": len(getattr(plugin_manager, "_installations", ())),
|
|
98
|
+
"installation_states": installation_state_counts,
|
|
87
99
|
"pending_registrations": len(
|
|
88
100
|
getattr(plugin_manager, "_pending_registrations", ())
|
|
89
101
|
),
|
|
@@ -287,6 +299,49 @@ class RuntimeContext:
|
|
|
287
299
|
self._installation_watermarks[binding.installation_uuid] = binding
|
|
288
300
|
return binding
|
|
289
301
|
|
|
302
|
+
def reconcile_installation_watermarks(
|
|
303
|
+
self,
|
|
304
|
+
authoritative_bindings: tuple[InstallationBinding, ...],
|
|
305
|
+
) -> None:
|
|
306
|
+
"""Drop inactive historical fences absent from an authoritative replay."""
|
|
307
|
+
|
|
308
|
+
for binding in self.inactive_installation_watermark_snapshots(
|
|
309
|
+
authoritative_bindings
|
|
310
|
+
):
|
|
311
|
+
self.drop_installation_watermark_if_current(binding)
|
|
312
|
+
|
|
313
|
+
def inactive_installation_watermark_snapshots(
|
|
314
|
+
self,
|
|
315
|
+
authoritative_bindings: tuple[InstallationBinding, ...],
|
|
316
|
+
) -> tuple[InstallationBinding, ...]:
|
|
317
|
+
"""Return inactive watermark values eligible for authoritative GC."""
|
|
318
|
+
|
|
319
|
+
authoritative_uuids = {
|
|
320
|
+
binding.installation_uuid for binding in authoritative_bindings
|
|
321
|
+
}
|
|
322
|
+
snapshots: list[InstallationBinding] = []
|
|
323
|
+
for installation_uuid, watermark in list(self._installation_watermarks.items()):
|
|
324
|
+
if installation_uuid in authoritative_uuids:
|
|
325
|
+
continue
|
|
326
|
+
if installation_uuid in self._installation_bindings:
|
|
327
|
+
continue
|
|
328
|
+
snapshots.append(watermark)
|
|
329
|
+
return tuple(snapshots)
|
|
330
|
+
|
|
331
|
+
def drop_installation_watermark_if_current(
|
|
332
|
+
self,
|
|
333
|
+
binding: InstallationBinding,
|
|
334
|
+
) -> bool:
|
|
335
|
+
"""Drop one inactive watermark only if it still matches ``binding``."""
|
|
336
|
+
|
|
337
|
+
current = self._installation_watermarks.get(binding.installation_uuid)
|
|
338
|
+
if current != binding:
|
|
339
|
+
return False
|
|
340
|
+
if binding.installation_uuid in self._installation_bindings:
|
|
341
|
+
return False
|
|
342
|
+
self._installation_watermarks.pop(binding.installation_uuid, None)
|
|
343
|
+
return True
|
|
344
|
+
|
|
290
345
|
def is_current_installation_binding(
|
|
291
346
|
self,
|
|
292
347
|
action_context: InstallationBinding | dict,
|
{langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/helper/marketplace.py
RENAMED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import asyncio
|
|
4
3
|
import json
|
|
5
4
|
import time
|
|
6
5
|
|
|
@@ -44,7 +43,7 @@ async def _request_json(client: httpx.AsyncClient, url: str) -> dict:
|
|
|
44
43
|
response,
|
|
45
44
|
max_bytes=_MAX_MARKETPLACE_JSON_BYTES,
|
|
46
45
|
)
|
|
47
|
-
payload =
|
|
46
|
+
payload = json.loads(body)
|
|
48
47
|
if not isinstance(payload, dict):
|
|
49
48
|
raise RuntimeError("Marketplace returned a non-object response")
|
|
50
49
|
if payload.get("code") != 0:
|
|
@@ -61,10 +60,7 @@ async def get_plugin_info(
|
|
|
61
60
|
)
|
|
62
61
|
async with httpx.AsyncClient(timeout=30) as client:
|
|
63
62
|
payload = await _request_json(client, url)
|
|
64
|
-
return
|
|
65
|
-
entities_marketplace.PluginInfo.model_validate,
|
|
66
|
-
payload["data"]["plugin"],
|
|
67
|
-
)
|
|
63
|
+
return entities_marketplace.PluginInfo.model_validate(payload["data"]["plugin"])
|
|
68
64
|
|
|
69
65
|
|
|
70
66
|
async def download_plugin(
|
|
@@ -532,10 +532,7 @@ async def ensure_plugin_environment(plugin_path: str) -> str:
|
|
|
532
532
|
if candidate.is_file():
|
|
533
533
|
return str(candidate.resolve())
|
|
534
534
|
|
|
535
|
-
|
|
536
|
-
venv.EnvBuilder(with_pip=True, system_site_packages=True).create,
|
|
537
|
-
root,
|
|
538
|
-
)
|
|
535
|
+
venv.EnvBuilder(with_pip=True, system_site_packages=True).create(root)
|
|
539
536
|
if not candidate.is_file():
|
|
540
537
|
raise RuntimeError(f"Plugin virtual environment was not created at {root}")
|
|
541
538
|
return str(candidate.resolve())
|
{langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/connections/stdio.py
RENAMED
|
@@ -39,7 +39,7 @@ class StdioConnection(connection.Connection):
|
|
|
39
39
|
async def send(self, message: str) -> None:
|
|
40
40
|
"""Send message with chunking support for large data."""
|
|
41
41
|
async with self._send_lock: # 确保同一时间只有一个send操作
|
|
42
|
-
message_bytes =
|
|
42
|
+
message_bytes = message.encode("utf-8")
|
|
43
43
|
message_size = len(message_bytes)
|
|
44
44
|
if message_size > MAX_MESSAGE_BYTES:
|
|
45
45
|
raise ValueError(
|
|
@@ -62,11 +62,7 @@ class StdioConnection(connection.Connection):
|
|
|
62
62
|
# For large messages, send in chunks
|
|
63
63
|
try:
|
|
64
64
|
del message_bytes
|
|
65
|
-
chunks =
|
|
66
|
-
split_utf8_chunks,
|
|
67
|
-
message,
|
|
68
|
-
self.chunk_size,
|
|
69
|
-
)
|
|
65
|
+
chunks = split_utf8_chunks(message, self.chunk_size)
|
|
70
66
|
# Send start marker for chunked message
|
|
71
67
|
chunk_header = json.dumps(
|
|
72
68
|
{"type": "chunk_start", "total_size": message_size}
|
|
@@ -216,10 +212,7 @@ class StdioConnection(connection.Connection):
|
|
|
216
212
|
"Incomplete runtime chunked message"
|
|
217
213
|
)
|
|
218
214
|
# Reconstruct original message
|
|
219
|
-
return
|
|
220
|
-
"".join,
|
|
221
|
-
chunks,
|
|
222
|
-
)
|
|
215
|
+
return "".join(chunks)
|
|
223
216
|
|
|
224
217
|
# Yield control periodically
|
|
225
218
|
if len(chunks) % 50 == 0:
|
{langbot_plugin-0.5.1 → langbot_plugin-0.5.2}/src/langbot_plugin/runtime/io/connections/ws.py
RENAMED
|
@@ -30,7 +30,7 @@ class WebSocketConnection(io_connection.Connection):
|
|
|
30
30
|
async def send(self, message: str) -> None:
|
|
31
31
|
"""Send message with chunking support for large data."""
|
|
32
32
|
async with self._send_lock: # 确保同一时间只有一个send操作
|
|
33
|
-
message_bytes =
|
|
33
|
+
message_bytes = message.encode("utf-8")
|
|
34
34
|
message_size = len(message_bytes)
|
|
35
35
|
if message_size > MAX_MESSAGE_BYTES:
|
|
36
36
|
raise ValueError(
|
|
@@ -55,11 +55,7 @@ class WebSocketConnection(io_connection.Connection):
|
|
|
55
55
|
# an independent message forces the receiver to guess message
|
|
56
56
|
# boundaries and permits unbounded cross-message accumulation.
|
|
57
57
|
del message_bytes
|
|
58
|
-
chunks =
|
|
59
|
-
split_utf8_chunks,
|
|
60
|
-
message,
|
|
61
|
-
self.chunk_size,
|
|
62
|
-
)
|
|
58
|
+
chunks = split_utf8_chunks(message, self.chunk_size)
|
|
63
59
|
await self.websocket.send(chunks)
|
|
64
60
|
except WebSocketClosed:
|
|
65
61
|
raise ConnectionClosedError("Connection closed during send")
|
|
@@ -89,7 +85,7 @@ class WebSocketConnection(io_connection.Connection):
|
|
|
89
85
|
# recv_streaming yields exactly one WebSocket message. JSON
|
|
90
86
|
# validation belongs to Handler; never concatenate separate peer
|
|
91
87
|
# messages while waiting for one that happens to parse.
|
|
92
|
-
return
|
|
88
|
+
return "".join(message_chunks)
|
|
93
89
|
|
|
94
90
|
except WebSocketClosed:
|
|
95
91
|
raise ConnectionClosedError("Connection closed")
|
|
@@ -19,8 +19,6 @@ import uuid
|
|
|
19
19
|
import contextlib
|
|
20
20
|
import contextvars
|
|
21
21
|
import re
|
|
22
|
-
import aiofiles
|
|
23
|
-
import aiofiles.os
|
|
24
22
|
import logging
|
|
25
23
|
from langbot_plugin.runtime.io import connection
|
|
26
24
|
from langbot_plugin.entities.io.req import ActionRequest
|
|
@@ -40,6 +38,7 @@ from langbot_plugin.runtime.security import PLUGIN_RUNTIME_PROFILE_ENV
|
|
|
40
38
|
from langbot_plugin.runtime.bounded_executor import blocking_work_scope
|
|
41
39
|
|
|
42
40
|
logger = logging.getLogger(__name__)
|
|
41
|
+
_ORIGINAL_ASYNCIO_TO_THREAD = asyncio.to_thread
|
|
43
42
|
|
|
44
43
|
FILE_STORAGE_DIR = "data/temp/lbp"
|
|
45
44
|
SHARED_WORKER_FILE_STORAGE_DIR = "/tmp/lbp-rpc"
|
|
@@ -52,6 +51,12 @@ _SAFE_FILE_KEY_PATTERN = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,254}$")
|
|
|
52
51
|
_SAFE_FILE_EXTENSION_PATTERN = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_-]{0,31}$")
|
|
53
52
|
|
|
54
53
|
|
|
54
|
+
async def _run_small_protocol_work(fn: Callable[..., Any], *args: Any) -> Any:
|
|
55
|
+
if asyncio.to_thread is _ORIGINAL_ASYNCIO_TO_THREAD:
|
|
56
|
+
return fn(*args)
|
|
57
|
+
return await asyncio.to_thread(fn, *args)
|
|
58
|
+
|
|
59
|
+
|
|
55
60
|
def _file_storage_path(
|
|
56
61
|
file_key: str,
|
|
57
62
|
file_storage_dir: str | os.PathLike[str] = FILE_STORAGE_DIR,
|
|
@@ -196,8 +201,8 @@ class Handler(abc.ABC):
|
|
|
196
201
|
and resulting_size > self.max_file_bytes
|
|
197
202
|
):
|
|
198
203
|
raise ValueError("File transfer exceeds the configured size limit")
|
|
199
|
-
|
|
200
|
-
|
|
204
|
+
with open(file_path, mode) as f:
|
|
205
|
+
f.write(chunk_bytes)
|
|
201
206
|
return ActionResponse.success({})
|
|
202
207
|
|
|
203
208
|
def _message_blocking_scope(
|
|
@@ -215,7 +220,7 @@ class Handler(abc.ABC):
|
|
|
215
220
|
"""Parse peer JSON outside the shared event loop with tenant fairness."""
|
|
216
221
|
|
|
217
222
|
with blocking_work_scope(self._message_blocking_scope()):
|
|
218
|
-
return await
|
|
223
|
+
return await _run_small_protocol_work(json.loads, message)
|
|
219
224
|
|
|
220
225
|
async def _encode_message(
|
|
221
226
|
self,
|
|
@@ -228,10 +233,11 @@ class Handler(abc.ABC):
|
|
|
228
233
|
with blocking_work_scope(
|
|
229
234
|
self._message_blocking_scope(action_context),
|
|
230
235
|
):
|
|
231
|
-
return await
|
|
232
|
-
lambda: json.dumps(
|
|
233
|
-
|
|
234
|
-
)
|
|
236
|
+
return await _run_small_protocol_work(
|
|
237
|
+
lambda value: json.dumps(
|
|
238
|
+
value.model_dump() if hasattr(value, "model_dump") else value
|
|
239
|
+
),
|
|
240
|
+
payload,
|
|
235
241
|
)
|
|
236
242
|
|
|
237
243
|
async def _validate_message_model(
|
|
@@ -242,7 +248,7 @@ class Handler(abc.ABC):
|
|
|
242
248
|
"""Run potentially deep Pydantic validation outside the event loop."""
|
|
243
249
|
|
|
244
250
|
with blocking_work_scope(self._message_blocking_scope()):
|
|
245
|
-
return await
|
|
251
|
+
return await _run_small_protocol_work(model_type.model_validate, payload)
|
|
246
252
|
|
|
247
253
|
async def _send_message(
|
|
248
254
|
self,
|
|
@@ -272,7 +278,7 @@ class Handler(abc.ABC):
|
|
|
272
278
|
return f"{exc.__class__.__name__}: {message}"
|
|
273
279
|
|
|
274
280
|
with blocking_work_scope(self._message_blocking_scope()):
|
|
275
|
-
return await
|
|
281
|
+
return await _run_small_protocol_work(render)
|
|
276
282
|
|
|
277
283
|
def set_disconnect_callback(
|
|
278
284
|
self,
|
|
@@ -793,16 +799,13 @@ class Handler(abc.ABC):
|
|
|
793
799
|
file_path = _file_storage_path(file_key, self.file_storage_dir)
|
|
794
800
|
if self.max_file_bytes is not None:
|
|
795
801
|
try:
|
|
796
|
-
file_size =
|
|
802
|
+
file_size = os.path.getsize(file_path)
|
|
797
803
|
except FileNotFoundError:
|
|
798
804
|
raise
|
|
799
805
|
if file_size > self.max_file_bytes:
|
|
800
806
|
raise ValueError("File transfer exceeds the configured size limit")
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
"rb",
|
|
804
|
-
) as f:
|
|
805
|
-
content = await f.read(
|
|
807
|
+
with open(file_path, "rb") as f:
|
|
808
|
+
content = f.read(
|
|
806
809
|
self.max_file_bytes + 1 if self.max_file_bytes is not None else -1
|
|
807
810
|
)
|
|
808
811
|
if self.max_file_bytes is not None and len(content) > self.max_file_bytes:
|
|
@@ -812,9 +815,7 @@ class Handler(abc.ABC):
|
|
|
812
815
|
async def delete_local_file(self, file_key: str) -> None:
|
|
813
816
|
async with self._file_transfer_lock:
|
|
814
817
|
try:
|
|
815
|
-
|
|
816
|
-
_file_storage_path(file_key, self.file_storage_dir)
|
|
817
|
-
)
|
|
818
|
+
os.remove(_file_storage_path(file_key, self.file_storage_dir))
|
|
818
819
|
except FileNotFoundError:
|
|
819
820
|
pass
|
|
820
821
|
finally:
|
|
@@ -826,9 +827,7 @@ class Handler(abc.ABC):
|
|
|
826
827
|
self._owned_transfer_files.clear()
|
|
827
828
|
for file_key in file_keys:
|
|
828
829
|
try:
|
|
829
|
-
|
|
830
|
-
_file_storage_path(file_key, self.file_storage_dir)
|
|
831
|
-
)
|
|
830
|
+
os.remove(_file_storage_path(file_key, self.file_storage_dir))
|
|
832
831
|
except FileNotFoundError:
|
|
833
832
|
pass
|
|
834
833
|
except OSError as exc:
|
|
@@ -18,7 +18,6 @@ from packaging.requirements import InvalidRequirement, Requirement
|
|
|
18
18
|
from packaging.utils import canonicalize_name
|
|
19
19
|
|
|
20
20
|
from langbot_plugin.runtime.plugin.artifact import PluginArtifact
|
|
21
|
-
from langbot_plugin.runtime import bounded_executor
|
|
22
21
|
|
|
23
22
|
|
|
24
23
|
_ENVIRONMENT_SCHEMA_VERSION = 1
|
|
@@ -79,10 +78,7 @@ class PluginDependencyEnvironmentStore:
|
|
|
79
78
|
runtime_fingerprint: str,
|
|
80
79
|
installer: DependencyInstaller,
|
|
81
80
|
) -> PluginDependencyEnvironment:
|
|
82
|
-
requirements, requirements_digest =
|
|
83
|
-
self._read_requirements,
|
|
84
|
-
artifact,
|
|
85
|
-
)
|
|
81
|
+
requirements, requirements_digest = self._read_requirements(artifact)
|
|
86
82
|
digest = self._environment_digest(
|
|
87
83
|
artifact.digest,
|
|
88
84
|
requirements_digest,
|
|
@@ -95,13 +91,13 @@ class PluginDependencyEnvironmentStore:
|
|
|
95
91
|
runtime_fingerprint=runtime_fingerprint,
|
|
96
92
|
)
|
|
97
93
|
|
|
98
|
-
ready =
|
|
94
|
+
ready = self.get_ready(digest, expected=expected)
|
|
99
95
|
if ready is not None:
|
|
100
96
|
return ready
|
|
101
97
|
|
|
102
98
|
lock = self._prepare_locks.setdefault(digest, asyncio.Lock())
|
|
103
99
|
async with lock:
|
|
104
|
-
ready =
|
|
100
|
+
ready = self.get_ready(digest, expected=expected)
|
|
105
101
|
if ready is not None:
|
|
106
102
|
return ready
|
|
107
103
|
return await self._prepare_locked(
|
|
@@ -186,17 +182,7 @@ class PluginDependencyEnvironmentStore:
|
|
|
186
182
|
raise
|
|
187
183
|
return staging
|
|
188
184
|
|
|
189
|
-
|
|
190
|
-
try:
|
|
191
|
-
staging = await asyncio.shield(staging_task)
|
|
192
|
-
except asyncio.CancelledError:
|
|
193
|
-
staging = await staging_task
|
|
194
|
-
await bounded_executor.run_blocking_cleanup(
|
|
195
|
-
shutil.rmtree,
|
|
196
|
-
staging.root_path,
|
|
197
|
-
True,
|
|
198
|
-
)
|
|
199
|
-
raise
|
|
185
|
+
staging = create_staging()
|
|
200
186
|
temporary_root = staging.root_path
|
|
201
187
|
|
|
202
188
|
try:
|
|
@@ -204,75 +190,35 @@ class PluginDependencyEnvironmentStore:
|
|
|
204
190
|
# Validate structure before reading distribution metadata: a build
|
|
205
191
|
# backend is untrusted and could otherwise make the Runtime follow
|
|
206
192
|
# a link outside the staging tree while verifying its output.
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
staging.site_packages_path,
|
|
210
|
-
)
|
|
211
|
-
await bounded_executor.run_blocking_atomic(
|
|
212
|
-
self._validate_installed_requirements,
|
|
193
|
+
self._validate_tree_entries(staging.site_packages_path)
|
|
194
|
+
self._validate_installed_requirements(
|
|
213
195
|
staging.site_packages_path,
|
|
214
196
|
requirements,
|
|
215
197
|
)
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
)
|
|
220
|
-
await bounded_executor.run_blocking_atomic(
|
|
221
|
-
self._make_tree_read_only,
|
|
222
|
-
staging.site_packages_path,
|
|
223
|
-
)
|
|
224
|
-
await bounded_executor.run_blocking_atomic(
|
|
225
|
-
self._write_marker,
|
|
226
|
-
temporary_root / _READY_MARKER,
|
|
227
|
-
expected,
|
|
228
|
-
)
|
|
229
|
-
await bounded_executor.run_blocking_atomic(
|
|
230
|
-
temporary_root.chmod,
|
|
231
|
-
0o555,
|
|
232
|
-
)
|
|
198
|
+
shutil.rmtree(staging.scratch_path)
|
|
199
|
+
self._make_tree_read_only(staging.site_packages_path)
|
|
200
|
+
self._write_marker(temporary_root / _READY_MARKER, expected)
|
|
201
|
+
temporary_root.chmod(0o555)
|
|
233
202
|
|
|
234
203
|
target_root = self.environments_path / digest
|
|
235
204
|
try:
|
|
236
|
-
|
|
237
|
-
os.rename,
|
|
238
|
-
temporary_root,
|
|
239
|
-
target_root,
|
|
240
|
-
)
|
|
205
|
+
os.rename(temporary_root, target_root)
|
|
241
206
|
except OSError:
|
|
242
207
|
# Another Runtime process may have completed the exact same
|
|
243
208
|
# environment while this process was preparing its staging tree.
|
|
244
|
-
ready =
|
|
245
|
-
self.get_ready,
|
|
246
|
-
digest,
|
|
247
|
-
expected=expected,
|
|
248
|
-
)
|
|
209
|
+
ready = self.get_ready(digest, expected=expected)
|
|
249
210
|
if ready is None:
|
|
250
211
|
raise
|
|
251
|
-
|
|
252
|
-
shutil.rmtree,
|
|
253
|
-
temporary_root,
|
|
254
|
-
True,
|
|
255
|
-
)
|
|
212
|
+
shutil.rmtree(temporary_root, ignore_errors=True)
|
|
256
213
|
return ready
|
|
257
|
-
|
|
258
|
-
self._fsync_directory,
|
|
259
|
-
self.environments_path,
|
|
260
|
-
)
|
|
214
|
+
self._fsync_directory(self.environments_path)
|
|
261
215
|
|
|
262
|
-
ready =
|
|
263
|
-
self.get_ready,
|
|
264
|
-
digest,
|
|
265
|
-
expected=expected,
|
|
266
|
-
)
|
|
216
|
+
ready = self.get_ready(digest, expected=expected)
|
|
267
217
|
if ready is None: # pragma: no cover - publication invariant
|
|
268
218
|
raise RuntimeError("Dependency environment publication failed")
|
|
269
219
|
return ready
|
|
270
220
|
except BaseException:
|
|
271
|
-
|
|
272
|
-
shutil.rmtree,
|
|
273
|
-
temporary_root,
|
|
274
|
-
True,
|
|
275
|
-
)
|
|
221
|
+
shutil.rmtree(temporary_root, ignore_errors=True)
|
|
276
222
|
raise
|
|
277
223
|
|
|
278
224
|
@staticmethod
|