diff --git a/pkg/api/http/controller/groups/plugins.py b/pkg/api/http/controller/groups/plugins.py index 8f59986d..4ae03042 100644 --- a/pkg/api/http/controller/groups/plugins.py +++ b/pkg/api/http/controller/groups/plugins.py @@ -1,10 +1,11 @@ from __future__ import annotations - +import base64 import quart from .....core import taskmgr from .. import group +from langbot_plugin.runtime.plugin.mgr import PluginInstallSource @group.group_class('plugins', '/api/v1/plugins') @@ -100,7 +101,47 @@ class PluginsRouterGroup(group.RouterGroup): self.ap.plugin_mgr.install_plugin(data['source'], task_context=ctx), kind='plugin-operation', name='plugin-install-github', - label=f'安装插件 ...{short_source_str}', + label=f'Installing plugin from github ...{short_source_str}', + context=ctx, + ) + + return self.success(data={'task_id': wrapper.id}) + + @self.route('/install/marketplace', methods=['POST'], auth_type=group.AuthType.USER_TOKEN) + async def _() -> str: + data = await quart.request.json + + ctx = taskmgr.TaskContext.new() + wrapper = self.ap.task_mgr.create_user_task( + self.ap.plugin_connector.install_plugin(PluginInstallSource.MARKETPLACE, data, task_context=ctx), + kind='plugin-operation', + name='plugin-install-marketplace', + label=f'Installing plugin from marketplace ...{data}', + context=ctx, + ) + + return self.success(data={'task_id': wrapper.id}) + + @self.route('/install/local', methods=['POST'], auth_type=group.AuthType.USER_TOKEN) + async def _() -> str: + file = (await quart.request.files).get('file') + if file is None: + return self.http_status(400, -1, 'file is required') + + file_bytes = file.read() + + file_base64 = base64.b64encode(file_bytes).decode('utf-8') + + data = { + 'plugin_file': file_base64, + } + + ctx = taskmgr.TaskContext.new() + wrapper = self.ap.task_mgr.create_user_task( + self.ap.plugin_connector.install_plugin(PluginInstallSource.LOCAL, data, task_context=ctx), + kind='plugin-operation', + name='plugin-install-local', + label=f'Installing plugin from local ...{file.filename}', context=ctx, ) diff --git a/pkg/plugin/connector.py b/pkg/plugin/connector.py index 7103c13e..cf363c7c 100644 --- a/pkg/plugin/connector.py +++ b/pkg/plugin/connector.py @@ -17,6 +17,8 @@ from langbot_plugin.api.entities import context import langbot_plugin.runtime.io.connection as base_connection from langbot_plugin.api.definition.components.manifest import ComponentManifest from langbot_plugin.api.entities.builtin.command import context as command_context +from langbot_plugin.runtime.plugin.mgr import PluginInstallSource +from ..core import taskmgr class PluginRuntimeConnector: @@ -97,6 +99,14 @@ class PluginRuntimeConnector: async def initialize_plugins(self): pass + async def install_plugin( + self, + install_source: PluginInstallSource, + install_info: dict[str, Any], + task_context: taskmgr.TaskContext | None = None, + ): + return await self.handler.install_plugin(install_source.value, install_info) + async def list_plugins(self) -> list[dict[str, Any]]: return await self.handler.list_plugins() diff --git a/pkg/plugin/handler.py b/pkg/plugin/handler.py index 32cf859a..502db9cb 100644 --- a/pkg/plugin/handler.py +++ b/pkg/plugin/handler.py @@ -373,6 +373,17 @@ class RuntimeConnectionHandler(handler.Handler): timeout=10, ) + async def install_plugin(self, install_source: str, install_info: dict[str, Any]) -> dict[str, Any]: + """Install plugin""" + return await self.call_action( + LangBotToRuntimeAction.INSTALL_PLUGIN, + { + 'install_source': install_source, + 'install_info': install_info, + }, + timeout=10, + ) + async def list_plugins(self) -> list[dict[str, Any]]: """List plugins""" result = await self.call_action(