mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-07-21 20:06:06 +00:00
Merge pull request #1214 from RockChinQ/feat/tool-loaders
feat: tool loader abstraction
This commit is contained in:
@@ -16,7 +16,6 @@ class FuncOperator(operator.CommandOperator):
|
|||||||
|
|
||||||
all_functions = await self.ap.tool_mgr.get_all_functions(
|
all_functions = await self.ap.tool_mgr.get_all_functions(
|
||||||
plugin_enabled=True,
|
plugin_enabled=True,
|
||||||
plugin_status=plugin_context.RuntimeContainerStatus.INITIALIZED,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for func in all_functions:
|
for func in all_functions:
|
||||||
|
|||||||
@@ -54,7 +54,6 @@ class SessionManager:
|
|||||||
use_model=await self.ap.model_mgr.get_model_by_name(self.ap.provider_cfg.data['model']),
|
use_model=await self.ap.model_mgr.get_model_by_name(self.ap.provider_cfg.data['model']),
|
||||||
use_funcs=await self.ap.tool_mgr.get_all_functions(
|
use_funcs=await self.ap.tool_mgr.get_all_functions(
|
||||||
plugin_enabled=True,
|
plugin_enabled=True,
|
||||||
plugin_status=plugin_context.RuntimeContainerStatus.INITIALIZED,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
session.conversations.append(conversation)
|
session.conversations.append(conversation)
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import abc
|
||||||
|
import typing
|
||||||
|
|
||||||
|
from ...core import app, entities as core_entities
|
||||||
|
from . import entities as tools_entities
|
||||||
|
|
||||||
|
|
||||||
|
preregistered_loaders: list[typing.Type[ToolLoader]] = []
|
||||||
|
|
||||||
|
def loader_class(name: str):
|
||||||
|
"""注册一个工具加载器
|
||||||
|
"""
|
||||||
|
def decorator(cls: typing.Type[ToolLoader]) -> typing.Type[ToolLoader]:
|
||||||
|
cls.name = name
|
||||||
|
preregistered_loaders.append(cls)
|
||||||
|
return cls
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
class ToolLoader(abc.ABC):
|
||||||
|
"""工具加载器"""
|
||||||
|
|
||||||
|
name: str = None
|
||||||
|
|
||||||
|
ap: app.Application
|
||||||
|
|
||||||
|
def __init__(self, ap: app.Application):
|
||||||
|
self.ap = ap
|
||||||
|
|
||||||
|
async def initialize(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
async def get_tools(self, enabled: bool=True) -> list[tools_entities.LLMFunction]:
|
||||||
|
"""获取所有工具"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
async def has_tool(self, name: str) -> bool:
|
||||||
|
"""检查工具是否存在"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
async def invoke_tool(self, query: core_entities.Query, name: str, parameters: dict) -> typing.Any:
|
||||||
|
"""执行工具调用"""
|
||||||
|
pass
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import typing
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from .. import loader, entities as tools_entities
|
||||||
|
from ....core import app, entities as core_entities
|
||||||
|
from ....plugin import context as plugin_context
|
||||||
|
|
||||||
|
|
||||||
|
@loader.loader_class("plugin-tool-loader")
|
||||||
|
class PluginToolLoader(loader.ToolLoader):
|
||||||
|
"""插件工具加载器。
|
||||||
|
|
||||||
|
本加载器中不存储工具信息,仅负责从插件系统中获取工具信息。
|
||||||
|
"""
|
||||||
|
|
||||||
|
async def get_tools(self, enabled: bool=True) -> list[tools_entities.LLMFunction]:
|
||||||
|
|
||||||
|
# 从插件系统获取工具(内容函数)
|
||||||
|
all_functions: list[tools_entities.LLMFunction] = []
|
||||||
|
|
||||||
|
for plugin in self.ap.plugin_mgr.plugins(
|
||||||
|
enabled=enabled, status=plugin_context.RuntimeContainerStatus.INITIALIZED
|
||||||
|
):
|
||||||
|
all_functions.extend(plugin.content_functions)
|
||||||
|
|
||||||
|
return all_functions
|
||||||
|
|
||||||
|
async def has_tool(self, name: str) -> bool:
|
||||||
|
"""检查工具是否存在"""
|
||||||
|
for plugin in self.ap.plugin_mgr.plugins(
|
||||||
|
enabled=True, status=plugin_context.RuntimeContainerStatus.INITIALIZED
|
||||||
|
):
|
||||||
|
for function in plugin.content_functions:
|
||||||
|
if function.name == name:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def _get_function_and_plugin(
|
||||||
|
self, name: str
|
||||||
|
) -> typing.Tuple[tools_entities.LLMFunction, plugin_context.BasePlugin]:
|
||||||
|
"""获取函数和插件实例"""
|
||||||
|
for plugin in self.ap.plugin_mgr.plugins(
|
||||||
|
enabled=True, status=plugin_context.RuntimeContainerStatus.INITIALIZED
|
||||||
|
):
|
||||||
|
for function in plugin.content_functions:
|
||||||
|
if function.name == name:
|
||||||
|
return function, plugin.plugin_inst
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
async def invoke_tool(self, query: core_entities.Query, name: str, parameters: dict) -> typing.Any:
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
function, plugin = await self._get_function_and_plugin(name)
|
||||||
|
if function is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
parameters = parameters.copy()
|
||||||
|
|
||||||
|
parameters = {"query": query, **parameters}
|
||||||
|
|
||||||
|
return await function.func(plugin, **parameters)
|
||||||
|
except Exception as e:
|
||||||
|
self.ap.logger.error(f"执行函数 {name} 时发生错误: {e}")
|
||||||
|
traceback.print_exc()
|
||||||
|
return f"error occurred when executing function {name}: {e}"
|
||||||
|
finally:
|
||||||
|
plugin = None
|
||||||
|
|
||||||
|
for p in self.ap.plugin_mgr.plugins():
|
||||||
|
if function in p.content_functions:
|
||||||
|
plugin = p
|
||||||
|
break
|
||||||
|
|
||||||
|
if plugin is not None:
|
||||||
|
|
||||||
|
await self.ap.ctr_mgr.usage.post_function_record(
|
||||||
|
plugin={
|
||||||
|
"name": plugin.plugin_name,
|
||||||
|
"remote": plugin.plugin_source,
|
||||||
|
"version": plugin.plugin_version,
|
||||||
|
"author": plugin.plugin_author,
|
||||||
|
},
|
||||||
|
function_name=function.name,
|
||||||
|
function_description=function.description,
|
||||||
|
)
|
||||||
@@ -4,8 +4,9 @@ import typing
|
|||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from ...core import app, entities as core_entities
|
from ...core import app, entities as core_entities
|
||||||
from . import entities
|
from . import entities, loader as tools_loader
|
||||||
from ...plugin import context as plugin_context
|
from ...plugin import context as plugin_context
|
||||||
|
from .loaders import plugin
|
||||||
|
|
||||||
|
|
||||||
class ToolManager:
|
class ToolManager:
|
||||||
@@ -13,33 +14,26 @@ class ToolManager:
|
|||||||
|
|
||||||
ap: app.Application
|
ap: app.Application
|
||||||
|
|
||||||
|
loaders: list[tools_loader.ToolLoader]
|
||||||
|
|
||||||
def __init__(self, ap: app.Application):
|
def __init__(self, ap: app.Application):
|
||||||
self.ap = ap
|
self.ap = ap
|
||||||
self.all_functions = []
|
self.all_functions = []
|
||||||
|
self.loaders = []
|
||||||
|
|
||||||
async def initialize(self):
|
async def initialize(self):
|
||||||
pass
|
|
||||||
|
|
||||||
async def get_function_and_plugin(
|
for loader_cls in tools_loader.preregistered_loaders:
|
||||||
self, name: str
|
loader_inst = loader_cls(self.ap)
|
||||||
) -> typing.Tuple[entities.LLMFunction, plugin_context.BasePlugin]:
|
await loader_inst.initialize()
|
||||||
"""获取函数和插件实例"""
|
self.loaders.append(loader_inst)
|
||||||
for plugin in self.ap.plugin_mgr.plugins(
|
|
||||||
enabled=True, status=plugin_context.RuntimeContainerStatus.INITIALIZED
|
|
||||||
):
|
|
||||||
for function in plugin.content_functions:
|
|
||||||
if function.name == name:
|
|
||||||
return function, plugin.plugin_inst
|
|
||||||
return None, None
|
|
||||||
|
|
||||||
async def get_all_functions(self, plugin_enabled: bool=None, plugin_status: plugin_context.RuntimeContainerStatus=None) -> list[entities.LLMFunction]:
|
async def get_all_functions(self, plugin_enabled: bool=None) -> list[entities.LLMFunction]:
|
||||||
"""获取所有函数"""
|
"""获取所有函数"""
|
||||||
all_functions: list[entities.LLMFunction] = []
|
all_functions: list[entities.LLMFunction] = []
|
||||||
|
|
||||||
for plugin in self.ap.plugin_mgr.plugins(
|
for loader in self.loaders:
|
||||||
enabled=plugin_enabled, status=plugin_status
|
all_functions.extend(await loader.get_tools(plugin_enabled))
|
||||||
):
|
|
||||||
all_functions.extend(plugin.content_functions)
|
|
||||||
|
|
||||||
return all_functions
|
return all_functions
|
||||||
|
|
||||||
@@ -102,38 +96,8 @@ class ToolManager:
|
|||||||
) -> typing.Any:
|
) -> typing.Any:
|
||||||
"""执行函数调用"""
|
"""执行函数调用"""
|
||||||
|
|
||||||
try:
|
for loader in self.loaders:
|
||||||
|
if await loader.has_tool(name):
|
||||||
function, plugin = await self.get_function_and_plugin(name)
|
return await loader.invoke_tool(query, name, parameters)
|
||||||
if function is None:
|
else:
|
||||||
return None
|
raise ValueError(f"未找到工具: {name}")
|
||||||
|
|
||||||
parameters = parameters.copy()
|
|
||||||
|
|
||||||
parameters = {"query": query, **parameters}
|
|
||||||
|
|
||||||
return await function.func(plugin, **parameters)
|
|
||||||
except Exception as e:
|
|
||||||
self.ap.logger.error(f"执行函数 {name} 时发生错误: {e}")
|
|
||||||
traceback.print_exc()
|
|
||||||
return f"error occurred when executing function {name}: {e}"
|
|
||||||
finally:
|
|
||||||
plugin = None
|
|
||||||
|
|
||||||
for p in self.ap.plugin_mgr.plugins():
|
|
||||||
if function in p.content_functions:
|
|
||||||
plugin = p
|
|
||||||
break
|
|
||||||
|
|
||||||
if plugin is not None:
|
|
||||||
|
|
||||||
await self.ap.ctr_mgr.usage.post_function_record(
|
|
||||||
plugin={
|
|
||||||
"name": plugin.plugin_name,
|
|
||||||
"remote": plugin.plugin_source,
|
|
||||||
"version": plugin.plugin_version,
|
|
||||||
"author": plugin.plugin_author,
|
|
||||||
},
|
|
||||||
function_name=function.name,
|
|
||||||
function_description=function.description,
|
|
||||||
)
|
|
||||||
Reference in New Issue
Block a user