Functions Runtime¶
The functions runtime provides a way to define and run Python functions in a way that makes it easy to operate with function-calling LLMs.
Stateless functions¶
Functions that have no state can be defined in a way that is compatible with the runtime in the following way:
runtime = FunctionsRuntime()
# Define a function
@runtime.register_function
def add(a: int, b: int) -> int:
"""Sums two numbers.
:param a: The first number.
:param b: The second number.
"""
return a + b
Note
When using tools as part of a suite there is no need to manually instantiate the runtime and register functions with it. This is only useful if you want to run an agent that uses the functions runtime as a standalone tool.
The runtime can be used to run the function:
result, _ = runtime.run_function(
None,
"add",
{"a": 1, "b": 2},
)
assert result == 3
Stateful functions¶
We can also define functions that rely on a stateful environment. This environment can't be given as argument by the LLM, so it is injected by the runtime in the function call. This is done by using the Depends
class for the environment-related arguments when defining the function. Let's show an example where the environment contains a counter:
class Counter(BaseModel):
value: int
def increment(self) -> int:
self.value += 1
return self.value
def increment_by(self, n: int) -> int:
self.value += n
return self.value
class Environment(TaskEnvironment):
counter: Counter
@runtime.register_function
def increment_counter(
counter: Annotated[Counter, Depends("counter")],
) -> int:
"""Increments the counter by 1."""
return counter.increment()
def increment_counter_by(
counter: Annotated[Counter, Depends("counter")],
n: int,
) -> int:
"""Increments the counter by `n`.
:param n: The number to increment the counter by.
"""
return counter.increment_by(n)
runtime.register_function(increment_counter_by)
The runtime can be used to run the function:
environment = Environment(counter=Counter(value=1))
result, _ = runtime.run_function(
environment,
"increment_counter_by",
{"n": 5},
)
assert result == 6
Error handling¶
By default, the runtime will catch exceptions raised by the function and return them to the LLM. This is useful to let the LLM recover from errors.
_, error = runtime.run_function(
None,
"add",
{"a": 1, "b": "hello"},
)
assert error is not None
assert "ValidationError" in error
However, the runtime can also raise exceptions the raise_on_error
to the run_function
method is True
:
try:
_, error = runtime.run_function(
None,
"add",
{"a": 1, "b": "hello"},
raise_on_error=True,
)
except ValidationError:
...
else:
assert False, "Expected a validation error"