Skip to content

dspy.BaseLM

dspy.BaseLM(model, model_type='chat', temperature=0.0, max_tokens=1000, cache=True, num_retries=0, **kwargs)

Base class for handling LLM calls.

Most users can directly use the dspy.LM class, which is a subclass of BaseLM. Users can also implement their own subclasses of BaseLM to support custom LLM providers and inject custom logic. To do so, simply override the forward method and make sure the return format is identical to the OpenAI response format.

Subclasses whose state is captured by BaseLM.__init__ can use the default dump_state and load_state methods. Subclasses with extra persistent state should override both methods.

Examples:

from openai import OpenAI

import dspy


class MyLM(dspy.BaseLM):
    @property
    def supports_function_calling(self) -> bool:
        return self.model.startswith("openai/gpt-4o")

    @property
    def supports_reasoning(self) -> bool:
        return self.model.startswith("anthropic/claude-3-7")

    @property
    def supports_response_schema(self) -> bool:
        return self.model.startswith("openai/gpt-4o")

    @property
    def supported_params(self) -> set[str]:
        if self.model.startswith("openai/gpt-4o"):
            return {"response_format"}  # accepts response_format=...
        return set()

    def forward(self, prompt, messages=None, **kwargs):
        client = OpenAI()
        return client.chat.completions.create(
            model=self.model,
            messages=messages or [{"role": "user", "content": prompt}],
            **self.kwargs,
        )


lm = MyLM(model="gpt-4o-mini")
dspy.configure(lm=lm)
print(dspy.Predict("q->a")(q="Why did the chicken cross the kitchen?"))
Source code in .venv/lib/python3.14/site-packages/dspy/clients/base_lm.py
def __init__(
    self,
    model,
    model_type="chat",
    temperature=0.0,
    max_tokens=1000,
    cache=True,
    num_retries=0,
    **kwargs,
):
    self.model = model
    self.model_type = model_type
    self.cache = cache
    self.num_retries = num_retries
    self.kwargs = dict(temperature=temperature, max_tokens=max_tokens, **kwargs)
    self.history = []

Functions

__call__(prompt: str | None = None, messages: list[dict[str, Any]] | None = None, **kwargs) -> list[dict[str, Any] | str]

Source code in .venv/lib/python3.14/site-packages/dspy/clients/base_lm.py
@with_callbacks
def __call__(
    self,
    prompt: str | None = None,
    messages: list[dict[str, Any]] | None = None,
    **kwargs
) -> list[dict[str, Any] | str]:
    response = self.forward(prompt=prompt, messages=messages, **kwargs)
    outputs = self._process_lm_response(response, prompt, messages, **kwargs)

    return outputs

acall(prompt: str | None = None, messages: list[dict[str, Any]] | None = None, **kwargs) -> list[dict[str, Any] | str] async

Source code in .venv/lib/python3.14/site-packages/dspy/clients/base_lm.py
@with_callbacks
async def acall(
    self,
    prompt: str | None = None,
    messages: list[dict[str, Any]] | None = None,
    **kwargs
) -> list[dict[str, Any] | str]:
    response = await self.aforward(prompt=prompt, messages=messages, **kwargs)
    outputs = self._process_lm_response(response, prompt, messages, **kwargs)
    return outputs

aforward(prompt: str | None = None, messages: list[dict[str, Any]] | None = None, **kwargs) async

Async forward pass for the language model.

Subclasses must implement this method, and the response should be identical to either of the following formats:

Raises:

Type Description
ContextWindowExceededError

When the request fails because the input exceeds the model's context window. DSPy adapters and modules rely on this error to trigger fallback behavior (e.g. truncating the prompt and retrying). Each subclass is responsible for catching its provider's native error and re-raising it as dspy.ContextWindowExceededError.

Source code in .venv/lib/python3.14/site-packages/dspy/clients/base_lm.py
async def aforward(
    self,
    prompt: str | None = None,
    messages: list[dict[str, Any]] | None = None,
    **kwargs
):
    """Async forward pass for the language model.

    Subclasses must implement this method, and the response should be identical to either of the following formats:

    - [OpenAI response format](https://platform.openai.com/docs/api-reference/responses/object)
    - [OpenAI chat completion format](https://platform.openai.com/docs/api-reference/chat/object)
    - [OpenAI text completion format](https://platform.openai.com/docs/api-reference/completions/object)

    Raises:
        dspy.ContextWindowExceededError: When the request fails because the
            input exceeds the model's context window. DSPy adapters and
            modules rely on this error to trigger fallback behavior (e.g.
            truncating the prompt and retrying). Each subclass is
            responsible for catching its provider's native error and
            re-raising it as `dspy.ContextWindowExceededError`.
    """
    raise NotImplementedError("Subclasses must implement this method.")

copy(**kwargs)

Returns a copy of the language model with possibly updated parameters.

Any provided keyword arguments update the corresponding attributes or LM kwargs of the copy. For example, lm.copy(rollout_id=1, temperature=1.0) returns an LM whose requests use a different rollout ID at non-zero temperature to bypass cache collisions.

Source code in .venv/lib/python3.14/site-packages/dspy/clients/base_lm.py
def copy(self, **kwargs):
    """Returns a copy of the language model with possibly updated parameters.

    Any provided keyword arguments update the corresponding attributes or LM kwargs of
    the copy. For example, ``lm.copy(rollout_id=1, temperature=1.0)`` returns an LM whose
    requests use a different rollout ID at non-zero temperature to bypass cache collisions.
    """

    import copy

    new_instance = copy.deepcopy(self)
    new_instance.history = []

    for key, value in kwargs.items():
        if hasattr(self, key):
            setattr(new_instance, key, value)
        if (key in self.kwargs) or (not hasattr(self, key)):
            if value is None:
                new_instance.kwargs.pop(key, None)
            else:
                new_instance.kwargs[key] = value
    if hasattr(new_instance, "_warned_zero_temp_rollout"):
        new_instance._warned_zero_temp_rollout = False

    return new_instance

dump_state() -> dict[str, Any]

Return a sanitized reconstruction state for this LM.

Subclasses whose state is captured by BaseLM.__init__ can use this default. Subclasses with extra persistent state should override both dump_state and load_state.

Returns:

Type Description
dict[str, Any]

A dictionary that can be passed to BaseLM.load_state. The state

dict[str, Any]

excludes API keys.

Source code in .venv/lib/python3.14/site-packages/dspy/clients/base_lm.py
def dump_state(self) -> dict[str, Any]:
    """Return a sanitized reconstruction state for this LM.

    Subclasses whose state is captured by `BaseLM.__init__` can use this
    default. Subclasses with extra persistent state should override both
    `dump_state` and `load_state`.

    Returns:
        A dictionary that can be passed to `BaseLM.load_state`. The state
        excludes API keys.
    """
    filtered_kwargs = {key: value for key, value in self.kwargs.items() if key not in ("api_key", LM_CLASS_STATE_KEY)}
    return {
        LM_CLASS_STATE_KEY: f"{type(self).__module__}.{type(self).__qualname__}",
        "model": self.model,
        "model_type": self.model_type,
        "cache": self.cache,
        "num_retries": getattr(self, "num_retries", 0),
        **filtered_kwargs,
    }

forward(prompt: str | None = None, messages: list[dict[str, Any]] | None = None, **kwargs)

Forward pass for the language model.

Subclasses must implement this method, and the response should be identical to either of the following formats:

Raises:

Type Description
ContextWindowExceededError

When the request fails because the input exceeds the model's context window. DSPy adapters and modules rely on this error to trigger fallback behavior (e.g. truncating the prompt and retrying). Each subclass is responsible for catching its provider's native error and re-raising it as dspy.ContextWindowExceededError.

Source code in .venv/lib/python3.14/site-packages/dspy/clients/base_lm.py
def forward(
    self,
    prompt: str | None = None,
    messages: list[dict[str, Any]] | None = None,
    **kwargs
):
    """Forward pass for the language model.

    Subclasses must implement this method, and the response should be identical to either of the following formats:

    - [OpenAI response format](https://platform.openai.com/docs/api-reference/responses/object)
    - [OpenAI chat completion format](https://platform.openai.com/docs/api-reference/chat/object)
    - [OpenAI text completion format](https://platform.openai.com/docs/api-reference/completions/object)

    Raises:
        dspy.ContextWindowExceededError: When the request fails because the
            input exceeds the model's context window. DSPy adapters and
            modules rely on this error to trigger fallback behavior (e.g.
            truncating the prompt and retrying). Each subclass is
            responsible for catching its provider's native error and
            re-raising it as `dspy.ContextWindowExceededError`.
    """
    raise NotImplementedError("Subclasses must implement this method.")

inspect_history(n: int = 1, file: TextIO | None = None) -> None

Source code in .venv/lib/python3.14/site-packages/dspy/clients/base_lm.py
def inspect_history(self, n: int = 1, file: "TextIO | None" = None) -> None:
    pretty_print_history(self.history, n, file=file)

load_state(state: dict[str, Any], *, allow_custom_lm_class: bool = False) -> BaseLM classmethod

Reconstruct an LM from dump_state output.

Legacy states without a class marker load as dspy.LM. Custom LM classes must be importable by their module-qualified class path and are only loaded when allow_custom_lm_class=True.

Parameters:

Name Type Description Default
state dict[str, Any]

Serialized LM state produced by dump_state.

required
allow_custom_lm_class bool

If True, allow importing and loading custom BaseLM subclasses recorded in state. Enable only for trusted state.

False

Returns:

Type Description
BaseLM

The reconstructed LM instance.

Raises:

Type Description
ValueError

If state references a custom LM class and allow_custom_lm_class is False.

ImportError

If the serialized LM class cannot be imported.

TypeError

If the serialized class is not a BaseLM subclass.

Source code in .venv/lib/python3.14/site-packages/dspy/clients/base_lm.py
@classmethod
def load_state(cls, state: dict[str, Any], *, allow_custom_lm_class: bool = False) -> "BaseLM":
    """Reconstruct an LM from `dump_state` output.

    Legacy states without a class marker load as `dspy.LM`. Custom LM
    classes must be importable by their module-qualified class path and are
    only loaded when `allow_custom_lm_class=True`.

    Args:
        state: Serialized LM state produced by `dump_state`.
        allow_custom_lm_class: If True, allow importing and loading custom
            `BaseLM` subclasses recorded in `state`. Enable only for trusted
            state.

    Returns:
        The reconstructed LM instance.

    Raises:
        ValueError: If `state` references a custom LM class and
            `allow_custom_lm_class` is False.
        ImportError: If the serialized LM class cannot be imported.
        TypeError: If the serialized class is not a `BaseLM` subclass.
    """
    state = dict(state)
    class_path = state.pop(LM_CLASS_STATE_KEY, None)

    if cls is BaseLM:
        if class_path is None:
            # Legacy saved programs did not record the concrete LM class.
            from dspy.clients.lm import LM

            return LM(**state)

        if class_path != _BUILTIN_LM_CLASS_PATH and not allow_custom_lm_class:
            raise ValueError(
                f"Refusing to import custom serialized LM class `{class_path}`. "
                "Pass allow_unsafe_lm_state=True when loading trusted files to enable custom LM classes."
            )

        lm_cls = _import_lm_class(class_path)
        if not issubclass(lm_cls, BaseLM):
            raise TypeError(f"Serialized LM class `{class_path}` must be a subclass of dspy.BaseLM.")
        if "allow_custom_lm_class" in inspect.signature(lm_cls.load_state).parameters:
            return lm_cls.load_state(state, allow_custom_lm_class=allow_custom_lm_class)
        return lm_cls.load_state(state)

    return cls(**state)

update_history(entry)

Source code in .venv/lib/python3.14/site-packages/dspy/clients/base_lm.py
def update_history(self, entry):
    if settings.disable_history:
        return

    # Global LM history
    if len(GLOBAL_HISTORY) >= MAX_HISTORY_SIZE:
        GLOBAL_HISTORY.pop(0)

    GLOBAL_HISTORY.append(entry)

    if settings.max_history_size == 0:
        return

    # dspy.LM.history
    if len(self.history) >= settings.max_history_size:
        self.history.pop(0)

    self.history.append(entry)

    # Per-module history
    caller_modules = settings.caller_modules or []
    for module in caller_modules:
        if len(module.history) >= settings.max_history_size:
            module.history.pop(0)
        module.history.append(entry)