mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-07-21 11:56:09 +00:00
feat(plugin): basic communication
This commit is contained in:
+12
-4
@@ -29,17 +29,23 @@ class PluginRuntimeConnector:
|
|||||||
|
|
||||||
async def initialize(self):
|
async def initialize(self):
|
||||||
async def new_connection_callback(connection: stdio_connection.StdioConnection):
|
async def new_connection_callback(connection: stdio_connection.StdioConnection):
|
||||||
self.ap.logger.info('Connected to plugin runtime.')
|
self.handler = handler.RuntimeConnectionHandler(connection, self.ap)
|
||||||
self.handler = handler.RuntimeConnectionHandler(connection)
|
|
||||||
self.handler_task = asyncio.create_task(self.handler.run())
|
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
|
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']
|
ws_url = self.ap.instance_config.data['plugin']['runtime_ws_url']
|
||||||
ctrl = ws_client_controller.WebSocketClientController(
|
ctrl = ws_client_controller.WebSocketClientController(
|
||||||
ws_url=ws_url,
|
ws_url=ws_url,
|
||||||
)
|
)
|
||||||
await ctrl.run(new_connection_callback)
|
task = ctrl.run(new_connection_callback)
|
||||||
else: # stdio
|
else: # stdio
|
||||||
|
self.ap.logger.info('use stdio to connect to plugin runtime')
|
||||||
# cmd: lbp rt -s
|
# cmd: lbp rt -s
|
||||||
python_path = sys.executable
|
python_path = sys.executable
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
@@ -48,7 +54,9 @@ class PluginRuntimeConnector:
|
|||||||
args=['-m', 'langbot_plugin.cli.__init__', 'rt', '-s'],
|
args=['-m', 'langbot_plugin.cli.__init__', 'rt', '-s'],
|
||||||
env=env,
|
env=env,
|
||||||
)
|
)
|
||||||
await ctrl.run(new_connection_callback)
|
task = ctrl.run(new_connection_callback)
|
||||||
|
|
||||||
|
asyncio.create_task(task)
|
||||||
|
|
||||||
async def run(self):
|
async def run(self):
|
||||||
pass
|
pass
|
||||||
|
|||||||
+77
-81
@@ -2,13 +2,9 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import typing
|
import typing
|
||||||
import abc
|
import abc
|
||||||
import pydantic.v1 as pydantic
|
|
||||||
import enum
|
|
||||||
|
|
||||||
from . import events
|
from . import events
|
||||||
from ..provider.tools import entities as tools_entities
|
|
||||||
from ..core import app
|
from ..core import app
|
||||||
from ..discover import engine as discover_engine
|
|
||||||
from ..platform.types import message as platform_message
|
from ..platform.types import message as platform_message
|
||||||
from ..platform import adapter as platform_adapter
|
from ..platform import adapter as platform_adapter
|
||||||
|
|
||||||
@@ -285,104 +281,104 @@ class EventContext:
|
|||||||
EventContext.eid += 1
|
EventContext.eid += 1
|
||||||
|
|
||||||
|
|
||||||
class RuntimeContainerStatus(enum.Enum):
|
# class RuntimeContainerStatus(enum.Enum):
|
||||||
"""插件容器状态"""
|
# """插件容器状态"""
|
||||||
|
|
||||||
MOUNTED = 'mounted'
|
# MOUNTED = 'mounted'
|
||||||
"""已加载进内存,所有位于运行时记录中的 RuntimeContainer 至少是这个状态"""
|
# """已加载进内存,所有位于运行时记录中的 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[
|
# event_handlers: dict[
|
||||||
typing.Type[events.BaseEventModel],
|
# typing.Type[events.BaseEventModel],
|
||||||
typing.Callable[[BasePlugin, EventContext], typing.Awaitable[None]],
|
# 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:
|
# class Config:
|
||||||
arbitrary_types_allowed = True
|
# arbitrary_types_allowed = True
|
||||||
|
|
||||||
def model_dump(self, *args, **kwargs):
|
# def model_dump(self, *args, **kwargs):
|
||||||
return {
|
# return {
|
||||||
'name': self.plugin_name,
|
# 'name': self.plugin_name,
|
||||||
'label': self.plugin_label.to_dict(),
|
# 'label': self.plugin_label.to_dict(),
|
||||||
'description': self.plugin_description.to_dict(),
|
# 'description': self.plugin_description.to_dict(),
|
||||||
'version': self.plugin_version,
|
# 'version': self.plugin_version,
|
||||||
'author': self.plugin_author,
|
# 'author': self.plugin_author,
|
||||||
'repository': self.plugin_repository,
|
# 'repository': self.plugin_repository,
|
||||||
'main_file': self.main_file,
|
# 'main_file': self.main_file,
|
||||||
'pkg_path': self.pkg_path,
|
# 'pkg_path': self.pkg_path,
|
||||||
'enabled': self.enabled,
|
# 'enabled': self.enabled,
|
||||||
'priority': self.priority,
|
# 'priority': self.priority,
|
||||||
'config_schema': self.config_schema,
|
# 'config_schema': self.config_schema,
|
||||||
'event_handlers': {
|
# 'event_handlers': {
|
||||||
event_name.__name__: handler.__name__ for event_name, handler in self.event_handlers.items()
|
# event_name.__name__: handler.__name__ for event_name, handler in self.event_handlers.items()
|
||||||
},
|
# },
|
||||||
'tools': [
|
# 'tools': [
|
||||||
{
|
# {
|
||||||
'name': function.name,
|
# 'name': function.name,
|
||||||
'human_desc': function.human_desc,
|
# 'human_desc': function.human_desc,
|
||||||
'description': function.description,
|
# 'description': function.description,
|
||||||
'parameters': function.parameters,
|
# 'parameters': function.parameters,
|
||||||
'func': function.func.__name__,
|
# 'func': function.func.__name__,
|
||||||
}
|
# }
|
||||||
for function in self.tools
|
# for function in self.tools
|
||||||
],
|
# ],
|
||||||
'status': self.status.value,
|
# 'status': self.status.value,
|
||||||
}
|
# }
|
||||||
|
|||||||
@@ -1,7 +1,64 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
from langbot_plugin.runtime.io import handler
|
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):
|
class RuntimeConnectionHandler(handler.Handler):
|
||||||
"""Runtime connection 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:
|
def get_platform() -> str:
|
||||||
"""获取当前平台"""
|
"""获取当前平台"""
|
||||||
# 检查是不是在 docker 里
|
# 检查是不是在 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 'docker'
|
||||||
|
|
||||||
return sys.platform
|
return sys.platform
|
||||||
|
|||||||
Reference in New Issue
Block a user