mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-02 12:05:54 +00:00
feat(plugin): basic communication
This commit is contained in:
@@ -29,17 +29,23 @@ class PluginRuntimeConnector:
|
||||
|
||||
async def initialize(self):
|
||||
async def new_connection_callback(connection: stdio_connection.StdioConnection):
|
||||
self.ap.logger.info('Connected to plugin runtime.')
|
||||
self.handler = handler.RuntimeConnectionHandler(connection)
|
||||
self.handler = handler.RuntimeConnectionHandler(connection, self.ap)
|
||||
self.handler_task = asyncio.create_task(self.handler.run())
|
||||
_ = await self.handler.ping()
|
||||
self.ap.logger.info('Connected to plugin runtime.')
|
||||
await self.handler_task
|
||||
|
||||
task: asyncio.Task | None = None
|
||||
|
||||
if platform.get_platform() == 'docker': # use websocket
|
||||
self.ap.logger.info('use websocket to connect to plugin runtime')
|
||||
ws_url = self.ap.instance_config.data['plugin']['runtime_ws_url']
|
||||
ctrl = ws_client_controller.WebSocketClientController(
|
||||
ws_url=ws_url,
|
||||
)
|
||||
await ctrl.run(new_connection_callback)
|
||||
task = ctrl.run(new_connection_callback)
|
||||
else: # stdio
|
||||
self.ap.logger.info('use stdio to connect to plugin runtime')
|
||||
# cmd: lbp rt -s
|
||||
python_path = sys.executable
|
||||
env = os.environ.copy()
|
||||
@@ -48,7 +54,9 @@ class PluginRuntimeConnector:
|
||||
args=['-m', 'langbot_plugin.cli.__init__', 'rt', '-s'],
|
||||
env=env,
|
||||
)
|
||||
await ctrl.run(new_connection_callback)
|
||||
task = ctrl.run(new_connection_callback)
|
||||
|
||||
asyncio.create_task(task)
|
||||
|
||||
async def run(self):
|
||||
pass
|
||||
|
||||
@@ -2,13 +2,9 @@ from __future__ import annotations
|
||||
|
||||
import typing
|
||||
import abc
|
||||
import pydantic.v1 as pydantic
|
||||
import enum
|
||||
|
||||
from . import events
|
||||
from ..provider.tools import entities as tools_entities
|
||||
from ..core import app
|
||||
from ..discover import engine as discover_engine
|
||||
from ..platform.types import message as platform_message
|
||||
from ..platform import adapter as platform_adapter
|
||||
|
||||
@@ -285,104 +281,104 @@ class EventContext:
|
||||
EventContext.eid += 1
|
||||
|
||||
|
||||
class RuntimeContainerStatus(enum.Enum):
|
||||
"""插件容器状态"""
|
||||
# class RuntimeContainerStatus(enum.Enum):
|
||||
# """插件容器状态"""
|
||||
|
||||
MOUNTED = 'mounted'
|
||||
"""已加载进内存,所有位于运行时记录中的 RuntimeContainer 至少是这个状态"""
|
||||
# MOUNTED = 'mounted'
|
||||
# """已加载进内存,所有位于运行时记录中的 RuntimeContainer 至少是这个状态"""
|
||||
|
||||
INITIALIZED = 'initialized'
|
||||
"""已初始化"""
|
||||
# INITIALIZED = 'initialized'
|
||||
# """已初始化"""
|
||||
|
||||
|
||||
class RuntimeContainer(pydantic.BaseModel):
|
||||
"""运行时的插件容器
|
||||
# class RuntimeContainer(pydantic.BaseModel):
|
||||
# """运行时的插件容器
|
||||
|
||||
运行期间存储单个插件的信息
|
||||
"""
|
||||
# 运行期间存储单个插件的信息
|
||||
# """
|
||||
|
||||
plugin_name: str
|
||||
"""插件名称"""
|
||||
# plugin_name: str
|
||||
# """插件名称"""
|
||||
|
||||
plugin_label: discover_engine.I18nString
|
||||
"""插件标签"""
|
||||
# plugin_label: discover_engine.I18nString
|
||||
# """插件标签"""
|
||||
|
||||
plugin_description: discover_engine.I18nString
|
||||
"""插件描述"""
|
||||
# plugin_description: discover_engine.I18nString
|
||||
# """插件描述"""
|
||||
|
||||
plugin_version: str
|
||||
"""插件版本"""
|
||||
# plugin_version: str
|
||||
# """插件版本"""
|
||||
|
||||
plugin_author: str
|
||||
"""插件作者"""
|
||||
# plugin_author: str
|
||||
# """插件作者"""
|
||||
|
||||
plugin_repository: str
|
||||
"""插件源码地址"""
|
||||
# plugin_repository: str
|
||||
# """插件源码地址"""
|
||||
|
||||
main_file: str
|
||||
"""插件主文件路径"""
|
||||
# main_file: str
|
||||
# """插件主文件路径"""
|
||||
|
||||
pkg_path: str
|
||||
"""插件包路径"""
|
||||
# pkg_path: str
|
||||
# """插件包路径"""
|
||||
|
||||
plugin_class: typing.Type[BasePlugin] = None
|
||||
"""插件类"""
|
||||
# plugin_class: typing.Type[BasePlugin] = None
|
||||
# """插件类"""
|
||||
|
||||
enabled: typing.Optional[bool] = True
|
||||
"""是否启用"""
|
||||
# enabled: typing.Optional[bool] = True
|
||||
# """是否启用"""
|
||||
|
||||
priority: typing.Optional[int] = 0
|
||||
"""优先级"""
|
||||
# priority: typing.Optional[int] = 0
|
||||
# """优先级"""
|
||||
|
||||
config_schema: typing.Optional[list[dict]] = []
|
||||
"""插件配置模板"""
|
||||
# config_schema: typing.Optional[list[dict]] = []
|
||||
# """插件配置模板"""
|
||||
|
||||
plugin_config: typing.Optional[dict] = {}
|
||||
"""插件配置"""
|
||||
# plugin_config: typing.Optional[dict] = {}
|
||||
# """插件配置"""
|
||||
|
||||
plugin_inst: typing.Optional[BasePlugin] = None
|
||||
"""插件实例"""
|
||||
# plugin_inst: typing.Optional[BasePlugin] = None
|
||||
# """插件实例"""
|
||||
|
||||
event_handlers: dict[
|
||||
typing.Type[events.BaseEventModel],
|
||||
typing.Callable[[BasePlugin, EventContext], typing.Awaitable[None]],
|
||||
] = {}
|
||||
"""事件处理器"""
|
||||
# event_handlers: dict[
|
||||
# typing.Type[events.BaseEventModel],
|
||||
# typing.Callable[[BasePlugin, EventContext], typing.Awaitable[None]],
|
||||
# ] = {}
|
||||
# """事件处理器"""
|
||||
|
||||
tools: list[tools_entities.LLMFunction] = []
|
||||
"""内容函数"""
|
||||
# tools: list[tools_entities.LLMFunction] = []
|
||||
# """内容函数"""
|
||||
|
||||
status: RuntimeContainerStatus = RuntimeContainerStatus.MOUNTED
|
||||
"""插件状态"""
|
||||
# status: RuntimeContainerStatus = RuntimeContainerStatus.MOUNTED
|
||||
# """插件状态"""
|
||||
|
||||
class Config:
|
||||
arbitrary_types_allowed = True
|
||||
# class Config:
|
||||
# arbitrary_types_allowed = True
|
||||
|
||||
def model_dump(self, *args, **kwargs):
|
||||
return {
|
||||
'name': self.plugin_name,
|
||||
'label': self.plugin_label.to_dict(),
|
||||
'description': self.plugin_description.to_dict(),
|
||||
'version': self.plugin_version,
|
||||
'author': self.plugin_author,
|
||||
'repository': self.plugin_repository,
|
||||
'main_file': self.main_file,
|
||||
'pkg_path': self.pkg_path,
|
||||
'enabled': self.enabled,
|
||||
'priority': self.priority,
|
||||
'config_schema': self.config_schema,
|
||||
'event_handlers': {
|
||||
event_name.__name__: handler.__name__ for event_name, handler in self.event_handlers.items()
|
||||
},
|
||||
'tools': [
|
||||
{
|
||||
'name': function.name,
|
||||
'human_desc': function.human_desc,
|
||||
'description': function.description,
|
||||
'parameters': function.parameters,
|
||||
'func': function.func.__name__,
|
||||
}
|
||||
for function in self.tools
|
||||
],
|
||||
'status': self.status.value,
|
||||
}
|
||||
# def model_dump(self, *args, **kwargs):
|
||||
# return {
|
||||
# 'name': self.plugin_name,
|
||||
# 'label': self.plugin_label.to_dict(),
|
||||
# 'description': self.plugin_description.to_dict(),
|
||||
# 'version': self.plugin_version,
|
||||
# 'author': self.plugin_author,
|
||||
# 'repository': self.plugin_repository,
|
||||
# 'main_file': self.main_file,
|
||||
# 'pkg_path': self.pkg_path,
|
||||
# 'enabled': self.enabled,
|
||||
# 'priority': self.priority,
|
||||
# 'config_schema': self.config_schema,
|
||||
# 'event_handlers': {
|
||||
# event_name.__name__: handler.__name__ for event_name, handler in self.event_handlers.items()
|
||||
# },
|
||||
# 'tools': [
|
||||
# {
|
||||
# 'name': function.name,
|
||||
# 'human_desc': function.human_desc,
|
||||
# 'description': function.description,
|
||||
# 'parameters': function.parameters,
|
||||
# 'func': function.func.__name__,
|
||||
# }
|
||||
# for function in self.tools
|
||||
# ],
|
||||
# 'status': self.status.value,
|
||||
# }
|
||||
|
||||
@@ -1,7 +1,64 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
import sqlalchemy
|
||||
|
||||
from langbot_plugin.runtime.io import handler
|
||||
from langbot_plugin.runtime.io.connection import Connection
|
||||
from langbot_plugin.entities.io.actions.enums import (
|
||||
CommonAction,
|
||||
RuntimeToLangBotAction,
|
||||
)
|
||||
|
||||
from ..entity.persistence import plugin as persistence_plugin
|
||||
|
||||
from ..core import app
|
||||
|
||||
|
||||
class RuntimeConnectionHandler(handler.Handler):
|
||||
"""Runtime connection handler"""
|
||||
|
||||
ap: app.Application
|
||||
|
||||
def __init__(self, connection: Connection, ap: app.Application):
|
||||
super().__init__(connection)
|
||||
self.ap = ap
|
||||
|
||||
@self.action(RuntimeToLangBotAction.GET_PLUGIN_SETTINGS)
|
||||
async def get_plugin_settings(data: dict[str, Any]) -> handler.ActionResponse:
|
||||
"""Get plugin settings"""
|
||||
|
||||
plugin_author = data['plugin_author']
|
||||
plugin_name = data['plugin_name']
|
||||
|
||||
result = await self.ap.persistence_mgr.execute_async(
|
||||
sqlalchemy.select(persistence_plugin.PluginSetting)
|
||||
.where(persistence_plugin.PluginSetting.plugin_author == plugin_author)
|
||||
.where(persistence_plugin.PluginSetting.plugin_name == plugin_name)
|
||||
)
|
||||
|
||||
data = {
|
||||
'enabled': False,
|
||||
'priority': 0,
|
||||
'plugin_config': {},
|
||||
}
|
||||
|
||||
setting = result.first()
|
||||
|
||||
if setting is not None:
|
||||
data['enabled'] = setting.enabled
|
||||
data['priority'] = setting.priority
|
||||
data['plugin_config'] = setting.config
|
||||
|
||||
return handler.ActionResponse.success(
|
||||
data=data,
|
||||
)
|
||||
|
||||
async def ping(self) -> dict[str, Any]:
|
||||
"""Ping the runtime"""
|
||||
return await self.call_action(
|
||||
CommonAction.PING,
|
||||
{},
|
||||
timeout=10,
|
||||
)
|
||||
|
||||
@@ -5,7 +5,10 @@ import sys
|
||||
def get_platform() -> str:
|
||||
"""获取当前平台"""
|
||||
# 检查是不是在 docker 里
|
||||
if os.path.exists('/.dockerenv'):
|
||||
|
||||
DOCKER_ENV = os.environ.get('DOCKER_ENV', 'false')
|
||||
|
||||
if os.path.exists('/.dockerenv') or DOCKER_ENV == 'true':
|
||||
return 'docker'
|
||||
|
||||
return sys.platform
|
||||
|
||||
Reference in New Issue
Block a user