mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-08-31 22:57:14 +00:00
feat: command execution via plugin
This commit is contained in:
@@ -56,3 +56,24 @@ class SystemRouterGroup(group.RouterGroup):
|
|||||||
return self.success(
|
return self.success(
|
||||||
data=await self.ap.tool_mgr.execute_func_call(data['tool_name'], data['tool_parameters'])
|
data=await self.ap.tool_mgr.execute_func_call(data['tool_name'], data['tool_parameters'])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@self.route('/debug/plugin/action', methods=['POST'], auth_type=group.AuthType.USER_TOKEN)
|
||||||
|
async def _() -> str:
|
||||||
|
if not constants.debug_mode:
|
||||||
|
return self.http_status(403, 403, 'Forbidden')
|
||||||
|
|
||||||
|
data = await quart.request.json
|
||||||
|
|
||||||
|
class AnoymousAction:
|
||||||
|
value = 'anonymous_action'
|
||||||
|
|
||||||
|
def __init__(self, value: str):
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
resp = await self.ap.plugin_connector.handler.call_action(
|
||||||
|
AnoymousAction(data['action']),
|
||||||
|
data['data'],
|
||||||
|
timeout=data.get('timeout', 10),
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.success(data=resp)
|
||||||
|
|||||||
+13
-27
@@ -16,13 +16,11 @@ importutil.import_modules_in_pkg(operators)
|
|||||||
|
|
||||||
|
|
||||||
class CommandManager:
|
class CommandManager:
|
||||||
"""命令管理器"""
|
|
||||||
|
|
||||||
ap: app.Application
|
ap: app.Application
|
||||||
|
|
||||||
cmd_list: list[operator.CommandOperator]
|
cmd_list: list[operator.CommandOperator]
|
||||||
"""
|
"""
|
||||||
运行时命令列表,扁平存储,各个对象包含对应的子节点引用
|
Runtime command list, flat storage, each object contains a reference to the corresponding child node
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, ap: app.Application):
|
def __init__(self, ap: app.Application):
|
||||||
@@ -64,30 +62,15 @@ class CommandManager:
|
|||||||
) -> typing.AsyncGenerator[command_context.CommandReturn, None]:
|
) -> typing.AsyncGenerator[command_context.CommandReturn, None]:
|
||||||
"""执行命令"""
|
"""执行命令"""
|
||||||
|
|
||||||
found = False
|
command_list = await self.ap.plugin_connector.list_commands()
|
||||||
if len(context.crt_params) > 0: # 查找下一个参数是否对应此节点的某个子节点名
|
|
||||||
for oper in operator_list:
|
|
||||||
if (context.crt_params[0] == oper.name or context.crt_params[0] in oper.alias) and (
|
|
||||||
oper.parent_class is None or oper.parent_class == operator.__class__
|
|
||||||
):
|
|
||||||
found = True
|
|
||||||
|
|
||||||
context.crt_command = context.crt_params[0]
|
for command in command_list:
|
||||||
context.crt_params = context.crt_params[1:]
|
if command.metadata.name == context.command:
|
||||||
|
async for ret in self.ap.plugin_connector.execute_command(context):
|
||||||
async for ret in self._execute(context, oper.children, oper):
|
yield ret
|
||||||
yield ret
|
break
|
||||||
break
|
else:
|
||||||
|
yield command_context.CommandReturn(error=command_errors.CommandNotFoundError(context.command))
|
||||||
if not found: # 如果下一个参数未在此节点的子节点中找到,则执行此节点或者报错
|
|
||||||
if operator is None:
|
|
||||||
yield command_context.CommandReturn(error=command_errors.CommandNotFoundError(context.crt_params[0]))
|
|
||||||
else:
|
|
||||||
if operator.lowest_privilege > context.privilege:
|
|
||||||
yield command_context.CommandReturn(error=command_errors.CommandPrivilegeError(operator.name))
|
|
||||||
else:
|
|
||||||
async for ret in operator.execute(context):
|
|
||||||
yield ret
|
|
||||||
|
|
||||||
async def execute(
|
async def execute(
|
||||||
self,
|
self,
|
||||||
@@ -103,7 +86,6 @@ class CommandManager:
|
|||||||
privilege = 2
|
privilege = 2
|
||||||
|
|
||||||
ctx = command_context.ExecuteContext(
|
ctx = command_context.ExecuteContext(
|
||||||
query=query,
|
|
||||||
session=session,
|
session=session,
|
||||||
command_text=command_text,
|
command_text=command_text,
|
||||||
command='',
|
command='',
|
||||||
@@ -113,5 +95,9 @@ class CommandManager:
|
|||||||
privilege=privilege,
|
privilege=privilege,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ctx.command = ctx.params[0]
|
||||||
|
|
||||||
|
ctx.shift()
|
||||||
|
|
||||||
async for ret in self._execute(ctx, self.cmd_list):
|
async for ret in self._execute(ctx, self.cmd_list):
|
||||||
yield ret
|
yield ret
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ from langbot_plugin.api.entities import events
|
|||||||
from langbot_plugin.api.entities import context
|
from langbot_plugin.api.entities import context
|
||||||
import langbot_plugin.runtime.io.connection as base_connection
|
import langbot_plugin.runtime.io.connection as base_connection
|
||||||
from langbot_plugin.api.definition.components.manifest import ComponentManifest
|
from langbot_plugin.api.definition.components.manifest import ComponentManifest
|
||||||
|
from langbot_plugin.api.entities.builtin.command import context as command_context
|
||||||
|
|
||||||
|
|
||||||
class PluginRuntimeConnector:
|
class PluginRuntimeConnector:
|
||||||
@@ -118,3 +119,18 @@ class PluginRuntimeConnector:
|
|||||||
|
|
||||||
async def call_tool(self, tool_name: str, parameters: dict[str, Any]) -> dict[str, Any]:
|
async def call_tool(self, tool_name: str, parameters: dict[str, Any]) -> dict[str, Any]:
|
||||||
return await self.handler.call_tool(tool_name, parameters)
|
return await self.handler.call_tool(tool_name, parameters)
|
||||||
|
|
||||||
|
async def list_commands(self) -> list[ComponentManifest]:
|
||||||
|
list_commands_data = await self.handler.list_commands()
|
||||||
|
|
||||||
|
return [ComponentManifest.model_validate(command) for command in list_commands_data]
|
||||||
|
|
||||||
|
async def execute_command(
|
||||||
|
self, command_ctx: command_context.ExecuteContext
|
||||||
|
) -> typing.AsyncGenerator[command_context.CommandReturn, None]:
|
||||||
|
gen = self.handler.execute_command(command_ctx.model_dump(serialize_as_any=True))
|
||||||
|
|
||||||
|
async for ret in gen:
|
||||||
|
cmd_ret = command_context.CommandReturn.model_validate(ret)
|
||||||
|
|
||||||
|
yield cmd_ret
|
||||||
|
|||||||
@@ -117,3 +117,25 @@ class RuntimeConnectionHandler(handler.Handler):
|
|||||||
)
|
)
|
||||||
|
|
||||||
return result['tool_response']
|
return result['tool_response']
|
||||||
|
|
||||||
|
async def list_commands(self) -> list[dict[str, Any]]:
|
||||||
|
"""List commands"""
|
||||||
|
result = await self.call_action(
|
||||||
|
LangBotToRuntimeAction.LIST_COMMANDS,
|
||||||
|
{},
|
||||||
|
timeout=10,
|
||||||
|
)
|
||||||
|
return result['commands']
|
||||||
|
|
||||||
|
async def execute_command(self, command_context: dict[str, Any]) -> typing.AsyncGenerator[dict[str, Any], None]:
|
||||||
|
"""Execute command"""
|
||||||
|
gen = self.call_action_generator(
|
||||||
|
LangBotToRuntimeAction.EXECUTE_COMMAND,
|
||||||
|
{
|
||||||
|
'command_context': command_context,
|
||||||
|
},
|
||||||
|
timeout=10,
|
||||||
|
)
|
||||||
|
|
||||||
|
async for ret in gen:
|
||||||
|
yield ret
|
||||||
|
|||||||
Reference in New Issue
Block a user