Source code for baybe.recommenders.meta.llm
"""Meta recommenders based on Large Language Models (LLMs)."""
from __future__ import annotations
import gc
from typing import Any
from attrs import define, field
from attrs.validators import instance_of
from baybe.recommenders.base import RecommenderProtocol
from baybe.recommenders.meta.sequential import (
SequentialMetaRecommender,
TwoPhaseMetaRecommender,
)
from baybe.recommenders.pure.llm.llm import LLMRecommender
[docs]
@define
class LLMTwoPhaseRecommender(TwoPhaseMetaRecommender):
"""A two-phase recommender that warm-starts with an LLM recommender.
A convenience specialization of
:class:`~baybe.recommenders.meta.sequential.TwoPhaseMetaRecommender` whose
:attr:`initial_recommender` must be an
:class:`~baybe.recommenders.pure.llm.llm.LLMRecommender`. The language model
proposes the first experiments, and the recommender switches to
:attr:`recommender` once :attr:`switch_after` measurements have been collected.
"""
initial_recommender: RecommenderProtocol = field(
validator=instance_of(LLMRecommender), kw_only=True
)
"""The language-model recommender used before the switch."""
[docs]
@classmethod
def from_model(
cls,
model: str,
experiment_description: str,
*,
litellm_args: dict[str, Any] | None = None,
switch_after: int = 1,
recommender: RecommenderProtocol | None = None,
) -> LLMTwoPhaseRecommender:
"""Create the recommender directly from a LiteLLM model identifier.
Args:
model: The LiteLLM model identifier for the initial LLM recommender.
experiment_description: Textual description of the experiment for the LLM.
litellm_args: Optional additional arguments passed to LiteLLM.
switch_after: The number of collected experiments after which the
recommender switches from the LLM to ``recommender``.
recommender: The recommender used after the switch. Defaults to a
:class:`~baybe.recommenders.pure.bayesian.botorch.core.BotorchRecommender`.
Returns:
The configured recommender.
"""
from baybe.recommenders.pure.bayesian.botorch import BotorchRecommender
return cls(
initial_recommender=LLMRecommender(
model=model,
experiment_description=experiment_description,
litellm_args=litellm_args or {},
),
recommender=BotorchRecommender() if recommender is None else recommender,
switch_after=switch_after,
)
[docs]
@define
class LLMAlternatingRecommender(SequentialMetaRecommender):
"""A recommender alternating between an LLM recommender and another one.
A convenience specialization of
:class:`~baybe.recommenders.meta.sequential.SequentialMetaRecommender` that cycles
between a language-model recommender and another recommender, advancing whenever
new measurements become available. The sequence must consist of exactly two
recommenders, the first of which must be an
:class:`~baybe.recommenders.pure.llm.llm.LLMRecommender`.
"""
def __attrs_post_init__(self) -> None:
"""Validate the recommender sequence.
Raises:
ValueError: If the sequence does not consist of exactly two recommenders.
ValueError: If the first recommender is not an
:class:`~baybe.recommenders.pure.llm.llm.LLMRecommender`.
"""
if len(self.recommenders) != 2:
raise ValueError(
f"'{self.__class__.__name__}' requires exactly two recommenders (an "
f"'{LLMRecommender.__name__}' alternating with another recommender), "
f"but {len(self.recommenders)} were provided."
)
if not isinstance(self.recommenders[0], LLMRecommender):
raise ValueError(
f"'{self.__class__.__name__}' requires the first recommender in the "
f"sequence to be an '{LLMRecommender.__name__}'."
)
if not self.mode == "cyclic":
raise ValueError(
f"'{self.__class__.__name__}' requires the mode to be 'cyclic'."
)
[docs]
@classmethod
def from_model(
cls,
model: str,
experiment_description: str,
*,
litellm_args: dict[str, Any] | None = None,
recommender: RecommenderProtocol | None = None,
) -> LLMAlternatingRecommender:
"""Create the recommender directly from a LiteLLM model identifier.
Args:
model: The LiteLLM model identifier for the LLM recommender.
experiment_description: Textual description of the experiment for the LLM.
litellm_args: Optional additional arguments passed to LiteLLM.
recommender: The recommender alternated with the LLM. Defaults to a
:class:`~baybe.recommenders.pure.bayesian.botorch.core.BotorchRecommender`.
Returns:
The configured recommender, cycling between the LLM and ``recommender``.
"""
from baybe.recommenders.pure.bayesian.botorch import BotorchRecommender
return cls(
recommenders=(
LLMRecommender(
model=model,
experiment_description=experiment_description,
litellm_args=litellm_args or {},
),
BotorchRecommender() if recommender is None else recommender,
),
mode="cyclic",
)
# Collect leftover original slotted classes processed by `attrs.define`
gc.collect()