feat: connector for plugin runtime

This commit is contained in:
Junyan Qin
2025-06-09 12:10:48 +08:00
parent af493c117c
commit 2d06f1cadb
9 changed files with 130 additions and 34 deletions

View File

@@ -13,6 +13,7 @@ from ..provider.tools import toolmgr as llm_tool_mgr
from ..config import manager as config_mgr
from ..command import cmdmgr
from ..plugin import manager as plugin_mgr
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
@@ -77,6 +78,8 @@ class Application:
plugin_mgr: plugin_mgr.PluginManager = None
plugin_connector: plugin_connector.PluginRuntimeConnector = None
query_pool: pool.QueryPool = None
ctrl: controller.Controller = None
@@ -117,6 +120,8 @@ class Application:
async def run(self):
try:
await self.plugin_connector.initialize_plugins()
await self.plugin_mgr.initialize_plugins()
# 后续可能会允许动态重启其他任务

View File

@@ -5,6 +5,7 @@ from .. import stage, app
from ...utils import version, proxy, announce
from ...pipeline import pool, controller, pipelinemgr
from ...plugin import manager as plugin_mgr
from ...plugin import connector as plugin_connector
from ...command import cmdmgr
from ...provider.session import sessionmgr as llm_session_mgr
from ...provider.modelmgr import modelmgr as llm_model_mgr
@@ -64,6 +65,10 @@ class BuildAppStage(stage.BootingStage):
ap.plugin_mgr = plugin_mgr_inst
await plugin_mgr_inst.load_plugins()
plugin_connector_inst = plugin_connector.PluginRuntimeConnector(ap)
await plugin_connector_inst.initialize()
ap.plugin_connector = plugin_connector_inst
cmd_mgr_inst = cmdmgr.CommandManager(ap)
await cmd_mgr_inst.initialize()
ap.cmd_mgr = cmd_mgr_inst

View File

@@ -44,6 +44,38 @@ class PersistenceManager:
await self.create_tables()
# run migrations
database_version = await self.execute_async(
sqlalchemy.select(metadata.Metadata).where(metadata.Metadata.key == 'database_version')
)
database_version = int(database_version.fetchone()[1])
required_database_version = constants.required_database_version
if database_version < required_database_version:
migrations = migration.preregistered_db_migrations
migrations.sort(key=lambda x: x.number)
last_migration_number = database_version
for migration_cls in migrations:
migration_instance = migration_cls(self.ap)
if (
migration_instance.number > database_version
and migration_instance.number <= required_database_version
):
await migration_instance.upgrade()
await self.execute_async(
sqlalchemy.update(metadata.Metadata)
.where(metadata.Metadata.key == 'database_version')
.values({'value': str(migration_instance.number)})
)
last_migration_number = migration_instance.number
self.ap.logger.info(f'Migration {migration_instance.number} completed.')
self.ap.logger.info(f'Successfully upgraded database to version {last_migration_number}.')
async def create_tables(self):
# create tables
async with self.get_db_engine().connect() as conn:
@@ -87,38 +119,6 @@ class PersistenceManager:
# =================================
# run migrations
database_version = await self.execute_async(
sqlalchemy.select(metadata.Metadata).where(metadata.Metadata.key == 'database_version')
)
database_version = int(database_version.fetchone()[1])
required_database_version = constants.required_database_version
if database_version < required_database_version:
migrations = migration.preregistered_db_migrations
migrations.sort(key=lambda x: x.number)
last_migration_number = database_version
for migration_cls in migrations:
migration_instance = migration_cls(self.ap)
if (
migration_instance.number > database_version
and migration_instance.number <= required_database_version
):
await migration_instance.upgrade()
await self.execute_async(
sqlalchemy.update(metadata.Metadata)
.where(metadata.Metadata.key == 'database_version')
.values({'value': str(migration_instance.number)})
)
last_migration_number = migration_instance.number
self.ap.logger.info(f'Migration {migration_instance.number} completed.')
self.ap.logger.info(f'Successfully upgraded database to version {last_migration_number}.')
async def execute_async(self, *args, **kwargs) -> sqlalchemy.engine.cursor.CursorResult:
async with self.get_db_engine().connect() as conn:
result = await conn.execute(*args, **kwargs)

View File

@@ -0,0 +1,20 @@
from .. import migration
@migration.migration_class(4)
class DBMigratePluginConfig(migration.DBMigration):
"""插件配置"""
async def upgrade(self):
"""升级"""
if 'plugin' not in self.ap.instance_config.data:
self.ap.instance_config.data['plugin'] = {
'runtime_ws_url': 'ws://localhost:5400/control/ws',
}
await self.ap.instance_config.dump_config()
async def downgrade(self):
"""降级"""
pass

57
pkg/plugin/connector.py Normal file
View File

@@ -0,0 +1,57 @@
# For connect to plugin runtime.
from __future__ import annotations
import asyncio
import os
import sys
from ..core import app
from . import handler
from ..utils import platform
from langbot_plugin.runtime.io.controllers.stdio import client as stdio_client_controller
from langbot_plugin.runtime.io.connections import stdio as stdio_connection
from langbot_plugin.runtime.io.controllers.ws import client as ws_client_controller
class PluginRuntimeConnector:
"""Plugin runtime connector"""
ap: app.Application
handler: handler.RuntimeConnectionHandler
handler_task: asyncio.Task
stdio_client_controller: stdio_client_controller.StdioClientController
def __init__(self, ap: app.Application):
self.ap = ap
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_task = asyncio.create_task(self.handler.run())
if platform.get_platform() == 'docker': # use websocket
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)
else: # stdio
# cmd: lbp rt -s
python_path = sys.executable
env = os.environ.copy()
ctrl = stdio_client_controller.StdioClientController(
command=python_path,
args=['-m', 'langbot_plugin.cli.__init__', 'rt', '-s'],
env=env,
)
await ctrl.run(new_connection_callback)
async def run(self):
pass
async def initialize_plugins(self):
pass

7
pkg/plugin/handler.py Normal file
View File

@@ -0,0 +1,7 @@
from __future__ import annotations
from langbot_plugin.runtime.io import handler
class RuntimeConnectionHandler(handler.Handler):
"""Runtime connection handler"""

View File

@@ -1,6 +1,6 @@
semantic_version = 'v4.0.7'
required_database_version = 3
required_database_version = 4
"""标记本版本所需要的数据库结构版本,用于判断数据库迁移"""
debug_mode = False

View File

@@ -50,7 +50,7 @@ dependencies = [
"pre-commit>=4.2.0",
"uv>=0.7.11",
"mypy>=1.16.0",
"langbot-plugin>=0.1.0a4",
"langbot-plugin==0.1.0a6",
]
keywords = [
"bot",

View File

@@ -18,3 +18,5 @@ system:
jwt:
expire: 604800
secret: ''
plugin:
runtime_ws_url: 'ws://plugin-runtime:5400/control/ws'