Skip to content

Variables

RuntimeVariable

RuntimeVariable is a wrapper around a variable that offers the following niceties:

  1. Automatic type-hinting of the variable. (Note that you can pass a type_hint to the constructor to override the type hint.)
  2. The variable can be updated over time through the update method. (Note that you need to specify the step argument , as it is not tracked automatically.)
  3. The variable history is tracked in the value_repr_history attribute, and you can inspect the history of the variable over time through the repr_at_step method. Note that this property returns a tuple of text and image representations. (See Storing variable representations for more details.)

Storing variable representations

Variables can be represented in two ways: as text and as images.

By default, we use Python's built-in repr function to store the text representations of the variables. This is done both for historical tracking, and because we send text data to the LLM. In order to avoid prompts that are too long, we truncate the text representations of the variables to a maximum length of 300 characters by default. (This can be changed by setting the max_var_repr_len argument in the constructor.)

Customizing the repr of variables

Classes that want to provide a custom repr method for the LLM should follow the LLMReprProvider protocol by implementing the llm_repr method.

Classes that want to provide a custom image and text repr for the LLM should follow the LLMImageReprProvider protocol by implementing the llm_image_repr method.

Handling new variable values

The update method allows you to update the value of the variable at a given step.

If you are not sure about whether the variable has actually been updated, you can use the skip_if_equal argument.

Examples

Handling a variable

from conatus.runtime.variable import RuntimeVariable

# Equivalent to 'my_var = 1'
var = RuntimeVariable("my_var", 1)
var.update(2, step=10)

# And now you can inspect the history of the variable over time
assert var.value_repr_history == [
    (0, ("1", None)),
    (10, ("2", None)),
]
assert var.repr_at_step(0) == ("1", None)
assert var.repr_at_step(10) == ("2", None)
assert var.repr_at_step(4) == ("1", None)

Providing a custom repr for the LLM

from conatus.runtime.variable import RuntimeVariable

class AnonymizedAPIKey:

    api_key: str

    def __init__(self, api_key: str) -> None:
        self.api_key = api_key

    def llm_repr(self) -> str:
        return self.api_key[:3] + "... (truncated)"

var = RuntimeVariable("my_var", AnonymizedAPIKey("1234567890"))
assert var.value_repr == ("123... (truncated)", None)

conatus.runtime.variable.RuntimeVariable

RuntimeVariable(
    name: str,
    value: ParamType,
    type_hint: TypeOfType | None = None,
    *,
    initial_step: int = 0,
    imported: bool = False,
    max_var_repr_len: int | None = None
)

Bases: ArbitraryBaseModel

Variable wrapper for the Runtime.

PARAMETER DESCRIPTION

name

The name of the variable.

TYPE: str

value

The value of the variable.

TYPE: ParamType

type_hint

The type hint of the variable.

TYPE: TypeOfType | None DEFAULT: None

initial_step

The step number at which the variable was initialized.

TYPE: int DEFAULT: 0

imported

Whether the variable is imported.

TYPE: bool DEFAULT: False

max_var_repr_len

The maximum length of the repr of the variable.

If not provided, will eventually default to DEFAULT_MAX_STR_REPR_LEN_PER_VARIABLE .

TYPE: int | None DEFAULT: None

METHOD DESCRIPTION
update

Update the value of the variable.

repr_at_step

Get the repr (text, image) of the variable at a given step.

__repr__

Override of the repr method for printing outside of LLM calls.

ATTRIBUTE DESCRIPTION
name

The name of the variable.

TYPE: str

type_hint

The type hint of the variable.

TYPE: TypeOfType

value

The value of the variable.

TYPE: ParamType

imported

Whether the variable was imported.

TYPE: bool

value_repr_history

List of all of the values the variable has taken on.

TYPE: list[tuple[int, tuple[str, str | None]]]

value_repr

The latest repr (text, image) of the variable.

TYPE: tuple[str, str | None]

Source code in conatus/runtime/variable.py
def __init__(
    self,
    name: str,
    value: ParamType,
    type_hint: TypeOfType | None = None,
    *,
    initial_step: int = 0,
    imported: bool = False,
    max_var_repr_len: int | None = None,
) -> None:
    """Initialize the RuntimeVariable.

    Args:
        name: The name of the variable.
        value: The value of the variable.
        type_hint: The type hint of the variable.
        initial_step: The step number at which the variable was initialized.
        imported: Whether the variable is imported.
        max_var_repr_len: The maximum length of the repr of the variable.

            If not provided, will eventually default to
            [`DEFAULT_MAX_STR_REPR_LEN_PER_VARIABLE`
            ][conatus.runtime.variable.DEFAULT_MAX_STR_REPR_LEN_PER_VARIABLE].
    """
    llm_repr_txt, llm_repr_img = llm_repr(value, max_var_repr_len)
    data = {
        "name": name,
        "type_hint": type_hint or type(value),
        "value": value,
        "imported": imported,
        "value_repr_history": [
            (initial_step, (llm_repr_txt, llm_repr_img))
        ],
    }
    super().__init__(**data)

name instance-attribute

name: str

The name of the variable.

type_hint instance-attribute

type_hint: TypeOfType

The type hint of the variable.

value instance-attribute

value: ParamType

The value of the variable.

imported instance-attribute

imported: bool

Whether the variable was imported.

"imported" here means that it was initialized at the start of the runtime, and that it was not initialized by an operation in the task run.

value_repr_history instance-attribute

value_repr_history: list[tuple[int, tuple[str, str | None]]]

List of all of the values the variable has taken on.

The list is of the following form:

[(step_number, (value_repr_txt, optional_value_repr_img)), ...]

To avoid memory issues, we only track the changes in the value of the variable. To find the value of the variable at a given step, we can use the repr_at_step method.

value_repr property

value_repr: tuple[str, str | None]

The latest repr (text, image) of the variable.

update

update(
    value: ParamType,
    step: int,
    *,
    skip_if_equal: bool = False,
    max_var_repr_len: int | None = None
) -> bool

Update the value of the variable.

If you want to ensure that the history is not polluted by duplicates, you can use the skip_if_equal argument.

We return a boolean indicating whether the variable was updated.

from conatus.runtime.variable import RuntimeVariable

var0 = RuntimeVariable("var0", 0)
var0.update(1, step=1)
assert var0.value_repr_history == [(0, ("0", None)), (1, ("1", None))]

# You can also ensure that the history is not polluted by duplicates
var0.update(1, step=2, skip_if_equal=True)
assert var0.value_repr_history == [(0, ("0", None)), (1, ("1", None))]

Warning

The skip_if_equal argument does not compare the values themselves, but rather the representations of the values as returned by repr or llm_repr . We do this so that mutable variables can be tracked over time.

This means, however, that using this argument will incur a performance cost, as we need to compute the repr of the value at each step.

PARAMETER DESCRIPTION

value

The new value of the variable.

TYPE: ParamType

step

The step number.

TYPE: int

skip_if_equal

If True, the variable will not be updated if the new value is equal to the current value. Because the value can be mutable, we compare the representations of the values instead of the values themselves.

TYPE: bool DEFAULT: False

max_var_repr_len

The maximum length of the repr of the variable. Note that since we only update if the repr has changed, changing this parameter over the lifetime of the variable might cause updates to happen when they otherwise wouldn't.

If not provided, will eventually default to DEFAULT_MAX_STR_REPR_LEN_PER_VARIABLE .

TYPE: int | None DEFAULT: None

RETURNS DESCRIPTION
bool

Whether the variable was updated.

TYPE: bool

Source code in conatus/runtime/variable.py
def update(
    self,
    value: ParamType,
    step: int,
    *,
    skip_if_equal: bool = False,
    max_var_repr_len: int | None = None,
) -> bool:
    """Update the value of the variable.

    If you want to ensure that the history is not polluted by duplicates,
    you can use the [`skip_if_equal`
    ][conatus.runtime.variable.RuntimeVariable.update(skip_if_equal)]
    argument.

    We return a boolean indicating whether the variable was updated.

    ```python
    from conatus.runtime.variable import RuntimeVariable

    var0 = RuntimeVariable("var0", 0)
    var0.update(1, step=1)
    assert var0.value_repr_history == [(0, ("0", None)), (1, ("1", None))]

    # You can also ensure that the history is not polluted by duplicates
    var0.update(1, step=2, skip_if_equal=True)
    assert var0.value_repr_history == [(0, ("0", None)), (1, ("1", None))]
    ```

    !!! warning

        The [`skip_if_equal`
        ][conatus.runtime.variable.RuntimeVariable.update(skip_if_equal)]
        argument does not compare the values themselves, but rather the
        representations of the values as returned by [`repr`][repr] or
        [`llm_repr`
        ][conatus.runtime.variable.LLMReprProvider.llm_repr]. We do this
        so that mutable variables can be tracked over time.

        This means, however, that using this argument will incur a
        performance cost, as we need to compute the repr of the value at
        each step.

    Args:
        value: The new value of the variable.
        step: The step number.
        skip_if_equal: If [`True`][True], the variable will not be updated
            if the new value is equal to the current value. Because the
            value can be mutable, we compare the representations of the
            values instead of the values themselves.
        max_var_repr_len: The maximum length of the repr of the variable.
            Note that since we only update if the repr has changed,
            changing this parameter over the lifetime of the variable
            might cause updates to happen when they otherwise wouldn't.

            If not provided, will eventually default to
            [`DEFAULT_MAX_STR_REPR_LEN_PER_VARIABLE`
            ][conatus.runtime.variable.DEFAULT_MAX_STR_REPR_LEN_PER_VARIABLE].

    Returns:
        bool: Whether the variable was updated.
    """
    llm_repr_txt, llm_repr_img = llm_repr(value, max_var_repr_len)
    if skip_if_equal and self.value_repr == (llm_repr_txt, llm_repr_img):
        return False
    self.value = value
    self.value_repr_history.append((step, (llm_repr_txt, llm_repr_img)))
    return True

repr_at_step

repr_at_step(step: int) -> tuple[str, str | None]

Get the repr (text, image) of the variable at a given step.

PARAMETER DESCRIPTION

step

The step number. Use -1 to get the latest value.

TYPE: int

RETURNS DESCRIPTION
str

The text repr of the value of the variable at the given step.

TYPE: str

str | None

str | None: The image repr of the value of the variable at the given step, if applicable.

RAISES DESCRIPTION
InvalidStepNumberError

If the step number is invalid, either because it is negative or because it is lower than the first step number in the value_repr_history .

Source code in conatus/runtime/variable.py
def repr_at_step(self, step: int) -> tuple[str, str | None]:
    """Get the repr (text, image) of the variable at a given step.

    Args:
        step: The step number. Use -1 to get the latest value.

    Returns:
        str: The text repr of the value of the variable at the given step.
        str | None: The image repr of the value of the variable at the
            given step, if applicable.

    Raises:
        InvalidStepNumberError: If the step number is invalid, either
            because it is negative or because it is lower than the first
            step number in the
            [`value_repr_history`
            ][conatus.runtime.variable.RuntimeVariable.value_repr_history].
    """
    # Start at the end of the list and work backwards until we find a step
    # number that is less than or equal to the given step.
    if step == -1:
        return self.value_repr
    if step < 0:
        msg = "Step number must be 0 or more, or -1 for the latest value."
        raise InvalidStepNumberError(msg)
    for step_number, value_repr in reversed(self.value_repr_history):
        if step_number <= step:
            return value_repr
    msg = f"The variable was not yet defined at step {step}."
    msg += "Variable history: \n"
    msg += str(self.value_repr_history)
    raise InvalidStepNumberError(msg)

__repr__

__repr__() -> str

Override of the repr method for printing outside of LLM calls.

Note that we hide the image repr for readability.

RETURNS DESCRIPTION
str

The repr of the variable.

TYPE: str

Source code in conatus/runtime/variable.py
@override
def __repr__(self) -> str:
    """Override of the [`repr`][repr] method for printing outside of LLM calls.

    Note that we hide the image repr for readability.

    Returns:
        str: The repr of the variable.
    """  # noqa: E501
    repr_value_repr_history = (", ").join(
        [
            f"({step}, {repr_txt}, "
            f"{'<b64img_str>' if repr_img else 'None'})"
            for step, (repr_txt, repr_img) in self.value_repr_history
        ]
    )
    return (
        "RuntimeVariable("
        f"name={self.name}, "
        f"value={self.value_repr[0]}, "
        f"type_hint={self.type_hint}, "
        f"imported={self.imported}, "
        f"value_repr_history=[{repr_value_repr_history}]"
        ")"
    )

Customized repr protocols

conatus.runtime.variable.LLMReprProvider

Bases: Protocol

Protocol for objects that can provide a custom repr for the LLM.

llm_repr abstractmethod

llm_repr() -> str

Get the repr of the object for the LLM.

Source code in conatus/runtime/variable.py
@abstractmethod
def llm_repr(self) -> str:
    """Get the repr of the object for the LLM."""
    ...  # pragma: no cover

conatus.runtime.variable.LLMImageReprProvider

Bases: Protocol

Protocol for objects whose custom repr is an image and a text.

llm_image_repr abstractmethod

llm_image_repr() -> tuple[str, str]

Get the repr of the object for the LLM.

Only JPEG in base64 are allowed

We only support JPEG images in base64, but this might change in the future.

RETURNS DESCRIPTION
str

The text caption.

str

The image data, encoded in JPEG and in base64.

Source code in conatus/runtime/variable.py
@abstractmethod
def llm_image_repr(self) -> tuple[str, str]:
    """Get the repr of the object for the LLM.

    !!! note "Only JPEG in base64 are allowed"

        We only support JPEG images in base64, but this might change in
        the future.

    Returns:
        (str): The text caption.
        (str): The image data, encoded in JPEG and in base64.
    """
    ...  # pragma: no cover

conatus.runtime.variable.llm_repr

llm_repr(
    var: ParamType, max_var_repr_len: int | None = None
) -> tuple[str, str | None]

Get the repr of the object for the LLM.

If the variable has a llm_repr or a llm_image_repr method, we use that. Otherwise, we fallback to the default repr function.

Note that max_var_repr_len is honored only for the text value of the variable.

PARAMETER DESCRIPTION
var

The variable to get the repr of.

TYPE: ParamType

max_var_repr_len

The maximum length of the repr of the variable.

TYPE: int | None DEFAULT: None

RETURNS DESCRIPTION
str

The repr of the variable.

TYPE: tuple[str, str | None]

Source code in conatus/runtime/variable.py
def llm_repr(
    var: ParamType, max_var_repr_len: int | None = None
) -> tuple[str, str | None]:
    """Get the repr of the object for the LLM.

    If the variable has a [`llm_repr`
    ][conatus.runtime.variable.LLMReprProvider.llm_repr] or a [`llm_image_repr`
    ][conatus.runtime.variable.LLMImageReprProvider.llm_image_repr] method,
    we use that. Otherwise, we fallback to the default [`repr`][repr] function.

    Note that `max_var_repr_len` is honored only for the text value of the
    variable.

    Args:
        var: The variable to get the repr of.
        max_var_repr_len: The maximum length of the repr of the variable.

    Returns:
        str: The repr of the variable.
    """
    repr_txt: str
    repr_img: str | None
    if max_var_repr_len is None:
        max_var_repr_len = DEFAULT_MAX_STR_REPR_LEN_PER_VARIABLE
    if isinstance(var, LLMImageReprProvider):
        repr_txt, repr_img = var.llm_image_repr()
    elif isinstance(var, LLMReprProvider):
        repr_txt = var.llm_repr()
        repr_img = None
    else:
        repr_txt = repr(var)
        repr_img = None
    if len(repr_txt) <= max_var_repr_len:
        return repr_txt, repr_img
    return (
        f"{repr_txt[:max_var_repr_len]}... "
        f"(truncated after {max_var_repr_len} characters)"
    ), repr_img

Default maximum lengths

conatus.utils.common_constants.DEFAULT_MAX_STR_REPR_LEN_PER_VARIABLE module-attribute

DEFAULT_MAX_STR_REPR_LEN_PER_VARIABLE = 300

The default maximum length of the repr of a variable.

conatus.utils.common_constants.DEFAULT_MAX_RESULT_REPR_LEN module-attribute

DEFAULT_MAX_RESULT_REPR_LEN = 2000

The default maximum length of the repr of a result.