Source code for smarter.apps.llm_client.models.llm_client_functions

"""All models for the OpenAI Function Calling API app."""

from typing import List, Optional

from django.db import models

from smarter.lib import logging
from smarter.lib.cache import cache_results
from smarter.lib.django.models import TimestampedModel
from smarter.lib.django.waffle import SmarterWaffleSwitches

from .llm_client import LLMClient

logger = logging.getSmarterLogger(__name__, any_switches=[SmarterWaffleSwitches.LLM_CLIENT_LOGGING])


[docs] class LLMClientFunctions(TimestampedModel): """ Represents the set of callable functions that are available to a LLMClient instance within the Smarter platform. This model is used to define and manage the specific functions that an llm_client can access or invoke during its operation. Each record in this model links an llm_client to a named function, enabling fine-grained control over the llm_client's capabilities. The available functions are defined by a fixed set of choices, such as "weather", "news", "prices", and "math". By associating functions with llm_clients, the platform allows for extensible and customizable llm_client behavior, supporting use cases where different llm_clients require access to different sets of features or integrations. This model is essential for scenarios where llm_clients need to perform actions, retrieve information, or interact with external APIs in a controlled and auditable manner. **Model Relationships** - Each LLMClientFunctions entry is linked to one :class:`LLMClient` instance. - Each entry specifies a function name from a predefined set of choices. **Usage Example** .. code-block:: python # Assign a function to an llm_client LLMClientFunctions.objects.create(llm_client=my_llm_client, name="weather") # List all functions available to an llm_client functions = LLMClientFunctions.objects.filter(llm_client=my_llm_client) **Notes** - The set of available functions is controlled by the ``CHOICES`` class attribute. - This model is intended for internal use to manage and audit llm_client capabilities. - Uniqueness is not enforced, so an llm_client may have multiple entries for the same function if needed. """ # pylint: disable=C0115 class Meta: verbose_name_plural = "LLMClient Functions" CHOICES = [ ("get_current_weather", "get_current_weather"), ("date_calculator", "date_calculator"), ("calculator", "calculator"), ] """ The set of available function names that can be assigned to a LLMClient. See Also: - :func:`smarter.apps.prompt.functions.function_weather.get_current_weather` - :func:`smarter.apps.prompt.functions.function_date_calculator.date_calculator` - :func:`smarter.apps.prompt.functions.function_calculator.calculator` """ #: The LLMClient instance associated with this function. #: Example: LLMClient(id=1, name="my-llm_client") llm_client = models.ForeignKey(LLMClient, on_delete=models.CASCADE) #: The name of the function available to the LLMClient. #: Example: "weather" name = models.CharField(max_length=255, choices=CHOICES, blank=True, null=True)
[docs] @classmethod def choices_list(cls): return [item[0] for item in cls.CHOICES]
[docs] @classmethod def functions(cls, llm_client: LLMClient) -> List[str]: """ Returns a list of function names associated with the given LLMClient. :param llm_client: The LLMClient instance to retrieve functions for. :returns: List of function names. :rtype: List[str] """ if not llm_client: return [] llm_client_functions = cls.objects.filter(llm_client=llm_client) retval = [llm_client_function.name for llm_client_function in llm_client_functions if llm_client_function.name] return retval
# pylint: disable=W0221
[docs] @classmethod def get_cached_objects( cls, invalidate: Optional[bool] = False, llm_client: Optional[LLMClient] = None ) -> models.QuerySet["LLMClientFunctions"]: """ Retrieve a queryset of LLMClientFunctions instances associated with a LLMClient using caching. :param invalidate: Whether to invalidate the cache for this retrieval. :type invalidate: bool, optional :param llm_client: The LLMClient instance for which to retrieve functions. :type llm_client: LLMClient, optional :returns: A queryset of LLMClientFunctions instances associated with the LLMClient. :rtype: models.QuerySet["LLMClientFunctions"] """ logger_prefix = logging.formatted_text(__name__ + "." + LLMClientFunctions.__name__ + ".get_cached_objects()") @cache_results(cls.cache_expiration) def _get_functions_for_llm_client_id( llm_client_id: int, class_name: str = cls.__name__ ) -> models.QuerySet["LLMClientFunctions"]: """ Caches the functions for an llm_client by llm_client_id to optimize. performance and reduce database queries. :param llm_client_id: The ID of the LLMClient for which to retrieve functions. :param class_name: The name of the class for cache key purposes. :returns: A queryset of LLMClientFunctions instances associated with the LLMClient. :rtype: models.QuerySet["LLMClientFunctions"] """ logger.debug("%s called with llm_client=%s, invalidate=%s", logger_prefix, llm_client, invalidate) retval = cls.objects.filter(llm_client_id=llm_client_id).select_related( "plugin_meta", "plugin_meta__user_profile", "plugin_meta__user_profile__user", "plugin_meta__user_profile__account", "llm_client__user_profile", "llm_client__user_profile__user", "llm_client__user_profile__account", ) logger.debug( "%s._get_functions_for_llm_client_id() fetched and cached %s functions for llm_client_id: %s", logger_prefix, len(retval), llm_client_id, ) return retval if invalidate and llm_client: _get_functions_for_llm_client_id.invalidate(llm_client_id=llm_client.id, class_name=cls.__name__) # type: ignore[union-attr] if llm_client: return _get_functions_for_llm_client_id(llm_client_id=llm_client.id, class_name=cls.__name__) # type: ignore[return-value] return super().get_cached_objects(invalidate=invalidate) # type: ignore[return-value]
__all__ = [ "LLMClientFunctions", ]