Behind the scenes
One of the aims of stores
is to provide a simple universal interface for tool-calling.
In order to achieve that, there are several decisions made "behind-the-scenes". This section describes some of these.
Formatting valid tool names
Both Anthropic and OpenAI restrict tool names to strings that are compliant with the regex ^[a-zA-Z0-9_-]{1,64}$
.
When loading tool indexes, stores
includes the module name in the tool name. Since .
is an invalid character, we substitute this with -
within Index.format_tools
if the provider belongs to Anthropic or OpenAI.
When the LLM returns a tool call that includes a substituted tool name, this is resolved automatically via Index.execute
. For example, running index.execute("foo-bar")
will correctly retrieve the foo.bar
tool.
Tools with optional arguments
Google Gemini's Automatic Function Calling feature allows developers to supply tools as a list of functions i.e. list[Callable]
, instead of a schema. However, this does not handle certain functions, including functions that have non-None
default argument values.
"""
As of writing (March 2025), these functions trigger an error
in Automatic Function Calling
"""
def foo(bar="test"):
pass
def foo(bar: str = "test"):
pass
def foo(bar: Optional[str] = "test"):
pass
# This function is fine
def foo(bar: Optional[str] = None):
pass
In order to handle this, stores
wraps all loaded tools in a function wrapper. The wrapper helps convert all non-None
default argument values to None
and injects the actual default value at runtime.
# Original function
def foo(bar="test"):
pass
# Simulated wrapped function (not actual implementation)
def wrapped_foo(bar: Optional[str] = None):
# Inject default value if None is passed
if bar == None: bar = "test"
return foo(bar)
One pitfall of this implementation is the following edge case, where None
is a valid argument value but it is not the default.
def foo(bar: None|str = "test"):
if bar == None:
# Do something special
return
else:
# Do something else
return
In this case, the wrapper will erroneously replace None
with "test"
at runtime.
If you have any suggestions on a better solution, send us a message!
Loading remote indexes
In order to make it easy for developers to share and use tools, tool indexes are installed with separate virtual environments. This helps to simplify dependency management and reduce dependency conflicts between different tool indexes.
Learn more about how remote indexes are installed in Remote indexes in detail.