llama-index
120 строк · 4.5 Кб
1"""Base tool spec class."""
2
3import asyncio4from inspect import signature5from typing import Any, Awaitable, Callable, Dict, List, Optional, Tuple, Type, Union6
7from llama_index.legacy.bridge.pydantic import BaseModel8from llama_index.legacy.tools.function_tool import FunctionTool9from llama_index.legacy.tools.types import ToolMetadata10from llama_index.legacy.tools.utils import create_schema_from_function11
12AsyncCallable = Callable[..., Awaitable[Any]]13
14
15# TODO: deprecate the Tuple (there's no use for it)
16SPEC_FUNCTION_TYPE = Union[str, Tuple[str, str]]17
18
19class BaseToolSpec:20"""Base tool spec class."""21
22# list of functions that you'd want to convert to spec23spec_functions: List[SPEC_FUNCTION_TYPE]24
25def get_fn_schema_from_fn_name(26self, fn_name: str, spec_functions: Optional[List[SPEC_FUNCTION_TYPE]] = None27) -> Optional[Type[BaseModel]]:28"""Return map from function name.29
30Return type is Optional, meaning that the schema can be None.
31In this case, it's up to the downstream tool implementation to infer the schema.
32
33"""
34spec_functions = spec_functions or self.spec_functions35for fn in spec_functions:36if fn == fn_name:37return create_schema_from_function(fn_name, getattr(self, fn_name))38
39raise ValueError(f"Invalid function name: {fn_name}")40
41def get_metadata_from_fn_name(42self, fn_name: str, spec_functions: Optional[List[SPEC_FUNCTION_TYPE]] = None43) -> Optional[ToolMetadata]:44"""Return map from function name.45
46Return type is Optional, meaning that the schema can be None.
47In this case, it's up to the downstream tool implementation to infer the schema.
48
49"""
50try:51func = getattr(self, fn_name)52except AttributeError:53return None54name = fn_name55docstring = func.__doc__ or ""56description = f"{name}{signature(func)}\n{docstring}"57fn_schema = self.get_fn_schema_from_fn_name(58fn_name, spec_functions=spec_functions59)60return ToolMetadata(name=name, description=description, fn_schema=fn_schema)61
62def to_tool_list(63self,64spec_functions: Optional[List[SPEC_FUNCTION_TYPE]] = None,65func_to_metadata_mapping: Optional[Dict[str, ToolMetadata]] = None,66) -> List[FunctionTool]:67"""Convert tool spec to list of tools."""68spec_functions = spec_functions or self.spec_functions69func_to_metadata_mapping = func_to_metadata_mapping or {}70tool_list = []71for func_spec in spec_functions:72func_sync = None73func_async = None74if isinstance(func_spec, str):75func = getattr(self, func_spec)76if asyncio.iscoroutinefunction(func):77func_async = func78else:79func_sync = func80metadata = func_to_metadata_mapping.get(func_spec, None)81if metadata is None:82metadata = self.get_metadata_from_fn_name(func_spec)83elif isinstance(func_spec, tuple) and len(func_spec) == 2:84func_sync = getattr(self, func_spec[0])85func_async = getattr(self, func_spec[1])86metadata = func_to_metadata_mapping.get(func_spec[0], None)87if metadata is None:88metadata = func_to_metadata_mapping.get(func_spec[1], None)89if metadata is None:90metadata = self.get_metadata_from_fn_name(func_spec[0])91else:92raise ValueError(93"spec_functions must be of type: List[Union[str, Tuple[str, str]]]"94)95
96if func_sync is None:97if func_async is not None:98func_sync = patch_sync(func_async)99else:100raise ValueError(101f"Could not retrieve a function for spec: {func_spec}"102)103
104tool = FunctionTool.from_defaults(105fn=func_sync,106async_fn=func_async,107tool_metadata=metadata,108)109tool_list.append(tool)110return tool_list111
112
113def patch_sync(func_async: AsyncCallable) -> Callable:114"""Patch sync function from async function."""115
116def patched_sync(*args: Any, **kwargs: Any) -> Any:117loop = asyncio.get_event_loop()118return loop.run_until_complete(func_async(*args, **kwargs))119
120return patched_sync121