From 214bc8ada9ff9802462bcd716fc8c4f19a509d98 Mon Sep 17 00:00:00 2001 From: Junyan Qin Date: Sun, 13 Jul 2025 20:45:45 +0800 Subject: [PATCH] feat: backward call apis --- pkg/api/http/service/bot.py | 16 +++++-- pkg/api/http/service/model.py | 12 ++++- pkg/persistence/mgr.py | 5 +- pkg/plugin/handler.py | 89 +++++++++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+), 7 deletions(-) diff --git a/pkg/api/http/service/bot.py b/pkg/api/http/service/bot.py index e5010007..a3987ba5 100644 --- a/pkg/api/http/service/bot.py +++ b/pkg/api/http/service/bot.py @@ -17,15 +17,19 @@ class BotService: def __init__(self, ap: app.Application) -> None: self.ap = ap - async def get_bots(self) -> list[dict]: + async def get_bots(self, include_secret: bool = True) -> list[dict]: """获取所有机器人""" result = await self.ap.persistence_mgr.execute_async(sqlalchemy.select(persistence_bot.Bot)) bots = result.all() - return [self.ap.persistence_mgr.serialize_model(persistence_bot.Bot, bot) for bot in bots] + masked_columns = [] + if not include_secret: + masked_columns = ['adapter_config'] - async def get_bot(self, bot_uuid: str) -> dict | None: + return [self.ap.persistence_mgr.serialize_model(persistence_bot.Bot, bot, masked_columns) for bot in bots] + + async def get_bot(self, bot_uuid: str, include_secret: bool = True) -> dict | None: """获取机器人""" result = await self.ap.persistence_mgr.execute_async( sqlalchemy.select(persistence_bot.Bot).where(persistence_bot.Bot.uuid == bot_uuid) @@ -36,7 +40,11 @@ class BotService: if bot is None: return None - return self.ap.persistence_mgr.serialize_model(persistence_bot.Bot, bot) + masked_columns = [] + if not include_secret: + masked_columns = ['adapter_config'] + + return self.ap.persistence_mgr.serialize_model(persistence_bot.Bot, bot, masked_columns) async def create_bot(self, bot_data: dict) -> str: """创建机器人""" diff --git a/pkg/api/http/service/model.py b/pkg/api/http/service/model.py index 3dab181e..e04a50b2 100644 --- a/pkg/api/http/service/model.py +++ b/pkg/api/http/service/model.py @@ -16,11 +16,19 @@ class ModelsService: def __init__(self, ap: app.Application) -> None: self.ap = ap - async def get_llm_models(self) -> list[dict]: + async def get_llm_models(self, include_secret: bool = True) -> list[dict]: result = await self.ap.persistence_mgr.execute_async(sqlalchemy.select(persistence_model.LLMModel)) models = result.all() - return [self.ap.persistence_mgr.serialize_model(persistence_model.LLMModel, model) for model in models] + + masked_columns = [] + if not include_secret: + masked_columns = ['api_keys'] + + return [ + self.ap.persistence_mgr.serialize_model(persistence_model.LLMModel, model, masked_columns) + for model in models + ] async def create_llm_model(self, model_data: dict) -> str: model_data['uuid'] = str(uuid.uuid4()) diff --git a/pkg/persistence/mgr.py b/pkg/persistence/mgr.py index 23f08aa7..33f71769 100644 --- a/pkg/persistence/mgr.py +++ b/pkg/persistence/mgr.py @@ -128,10 +128,13 @@ class PersistenceManager: def get_db_engine(self) -> sqlalchemy_asyncio.AsyncEngine: return self.db.get_engine() - def serialize_model(self, model: typing.Type[sqlalchemy.Base], data: sqlalchemy.Base) -> dict: + def serialize_model( + self, model: typing.Type[sqlalchemy.Base], data: sqlalchemy.Base, masked_columns: list[str] = [] + ) -> dict: return { column.name: getattr(data, column.name) if not isinstance(getattr(data, column.name), (datetime.datetime)) else getattr(data, column.name).isoformat() for column in model.__table__.columns + if column.name not in masked_columns } diff --git a/pkg/plugin/handler.py b/pkg/plugin/handler.py index 77fc46bd..c3fd9fe1 100644 --- a/pkg/plugin/handler.py +++ b/pkg/plugin/handler.py @@ -14,10 +14,13 @@ from langbot_plugin.entities.io.actions.enums import ( PluginToRuntimeAction, ) import langbot_plugin.api.entities.builtin.platform.message as platform_message +import langbot_plugin.api.entities.builtin.provider.message as provider_message +import langbot_plugin.api.entities.builtin.resource.tool as resource_tool from ..entity.persistence import plugin as persistence_plugin from ..core import app +from ..utils import constants class RuntimeConnectionHandler(handler.Handler): @@ -163,6 +166,92 @@ class RuntimeConnectionHandler(handler.Handler): }, ) + @self.action(PluginToRuntimeAction.GET_LANGBOT_VERSION) + async def get_langbot_version(data: dict[str, Any]) -> handler.ActionResponse: + """Get langbot version""" + return handler.ActionResponse.success( + data={ + 'version': constants.semantic_version, + }, + ) + + @self.action(PluginToRuntimeAction.GET_BOTS) + async def get_bots(data: dict[str, Any]) -> handler.ActionResponse: + """Get bots""" + bots = await self.ap.bot_service.get_bots(include_secret=False) + return handler.ActionResponse.success( + data={ + 'bots': bots, + }, + ) + + @self.action(PluginToRuntimeAction.SEND_MESSAGE) + async def send_message(data: dict[str, Any]) -> handler.ActionResponse: + """Send message""" + bot_uuid = data['bot_uuid'] + target_type = data['target_type'] + target_id = data['target_id'] + message_chain = data['message_chain'] + + message_chain_obj = platform_message.MessageChain.model_validate(message_chain) + + bot = await self.ap.platform_mgr.get_bot_by_uuid(bot_uuid) + if bot is None: + return handler.ActionResponse.error( + message=f'Bot with bot_uuid {bot_uuid} not found', + ) + + await bot.adapter.send_message( + target_type, + target_id, + message_chain_obj, + ) + + return handler.ActionResponse.success( + data={}, + ) + + @self.action(PluginToRuntimeAction.GET_LLM_MODELS) + async def get_llm_models(data: dict[str, Any]) -> handler.ActionResponse: + """Get llm models""" + llm_models = await self.ap.model_service.get_llm_models(include_secret=False) + return handler.ActionResponse.success( + data={ + 'llm_models': llm_models, + }, + ) + + @self.action(PluginToRuntimeAction.INVOKE_LLM) + async def invoke_llm(data: dict[str, Any]) -> handler.ActionResponse: + """Invoke llm""" + llm_model_uuid = data['llm_model_uuid'] + messages = data['messages'] + funcs = data.get('funcs', []) + extra_args = data.get('extra_args', {}) + + llm_model = await self.ap.model_mgr.get_model_by_uuid(llm_model_uuid) + if llm_model is None: + return handler.ActionResponse.error( + message=f'LLM model with llm_model_uuid {llm_model_uuid} not found', + ) + + messages_obj = [provider_message.Message.model_validate(message) for message in messages] + funcs_obj = [resource_tool.LLMTool.model_validate(func) for func in funcs] + + result = await llm_model.requester.invoke_llm( + query=None, + model=llm_model, + messages=messages_obj, + funcs=funcs_obj, + extra_args=extra_args, + ) + + return handler.ActionResponse.success( + data={ + 'message': result.model_dump(), + }, + ) + async def ping(self) -> dict[str, Any]: """Ping the runtime""" return await self.call_action(