Spaces:
Runtime error
Runtime error
from typing import Any, Dict, Optional, Callable, get_type_hints | |
from pydantic import BaseModel, validate_call | |
from phi.utils.log import logger | |
class Function(BaseModel): | |
"""Model for Functions""" | |
# The name of the function to be called. | |
# Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64. | |
name: str | |
# A description of what the function does, used by the model to choose when and how to call the function. | |
description: Optional[str] = None | |
# The parameters the functions accepts, described as a JSON Schema object. | |
# To describe a function that accepts no parameters, provide the value {"type": "object", "properties": {}}. | |
parameters: Dict[str, Any] = {"type": "object", "properties": {}} | |
entrypoint: Optional[Callable] = None | |
# If True, the arguments are sanitized before being passed to the function. | |
sanitize_arguments: bool = True | |
def to_dict(self) -> Dict[str, Any]: | |
return self.model_dump(exclude_none=True, include={"name", "description", "parameters"}) | |
def from_callable(cls, c: Callable) -> "Function": | |
from inspect import getdoc | |
from phi.utils.json_schema import get_json_schema | |
parameters = {"type": "object", "properties": {}} | |
try: | |
# logger.info(f"Getting type hints for {c}") | |
type_hints = get_type_hints(c) | |
# logger.info(f"Type hints for {c}: {type_hints}") | |
# logger.info(f"Getting JSON schema for {type_hints}") | |
parameters = get_json_schema(type_hints) | |
# logger.info(f"JSON schema for {c}: {parameters}") | |
# logger.debug(f"Type hints for {c.__name__}: {type_hints}") | |
except Exception as e: | |
logger.warning(f"Could not parse args for {c.__name__}: {e}") | |
return cls( | |
name=c.__name__, | |
description=getdoc(c), | |
parameters=parameters, | |
entrypoint=validate_call(c), | |
) | |
def get_type_name(self, t): | |
name = str(t) | |
if "list" in name or "dict" in name: | |
return name | |
else: | |
return t.__name__ | |
def get_definition_for_prompt(self) -> Optional[str]: | |
"""Returns a function definition that can be used in a prompt.""" | |
import json | |
if self.entrypoint is None: | |
return None | |
type_hints = get_type_hints(self.entrypoint) | |
return_type = type_hints.get("return", None) | |
returns = None | |
if return_type is not None: | |
returns = self.get_type_name(return_type) | |
function_info = { | |
"name": self.name, | |
"description": self.description, | |
"arguments": self.parameters.get("properties", {}), | |
"returns": returns, | |
} | |
return json.dumps(function_info, indent=2) | |
def get_definition_for_prompt_dict(self) -> Optional[Dict[str, Any]]: | |
"""Returns a function definition that can be used in a prompt.""" | |
if self.entrypoint is None: | |
return None | |
type_hints = get_type_hints(self.entrypoint) | |
return_type = type_hints.get("return", None) | |
returns = None | |
if return_type is not None: | |
returns = self.get_type_name(return_type) | |
function_info = { | |
"name": self.name, | |
"description": self.description, | |
"arguments": self.parameters.get("properties", {}), | |
"returns": returns, | |
} | |
return function_info | |
class FunctionCall(BaseModel): | |
"""Model for Function Calls""" | |
# The function to be called. | |
function: Function | |
# The arguments to call the function with. | |
arguments: Optional[Dict[str, Any]] = None | |
# The result of the function call. | |
result: Optional[Any] = None | |
# The ID of the function call. | |
call_id: Optional[str] = None | |
# Error while parsing arguments or running the function. | |
error: Optional[str] = None | |
def get_call_str(self) -> str: | |
"""Returns a string representation of the function call.""" | |
if self.arguments is None: | |
return f"{self.function.name}()" | |
trimmed_arguments = {} | |
for k, v in self.arguments.items(): | |
if isinstance(v, str) and len(v) > 100: | |
trimmed_arguments[k] = "..." | |
else: | |
trimmed_arguments[k] = v | |
call_str = f"{self.function.name}({', '.join([f'{k}={v}' for k, v in trimmed_arguments.items()])})" | |
return call_str | |
def execute(self) -> bool: | |
"""Runs the function call. | |
@return: True if the function call was successful, False otherwise. | |
""" | |
if self.function.entrypoint is None: | |
return False | |
logger.debug(f"Running: {self.get_call_str()}") | |
# Call the function with no arguments if none are provided. | |
if self.arguments is None: | |
try: | |
self.result = self.function.entrypoint() | |
return True | |
except Exception as e: | |
logger.warning(f"Could not run function {self.get_call_str()}") | |
logger.exception(e) | |
self.result = str(e) | |
return False | |
try: | |
self.result = self.function.entrypoint(**self.arguments) | |
return True | |
except Exception as e: | |
logger.warning(f"Could not run function {self.get_call_str()}") | |
logger.exception(e) | |
self.result = str(e) | |
return False | |