Tool Protocol

The protocol that a Smarter Extension’s tools implement.

A tool is a subclass of SmarterExtensionTool. It declares its name, the Pydantic model of its arguments, and a call() method:

from pydantic import BaseModel, Field

from smarter.apps.llmtool.extension import ExtensionContext, ExtensionToolError, SmarterExtensionTool

class WeatherArguments(BaseModel):
    location: str = Field(..., description="City and state, e.g. San Francisco, CA")

class GetWeather(SmarterExtensionTool):
    """Get the current weather and a 24-hour forecast for a location."""

    name = "get_weather"
    arguments_model = WeatherArguments

    def call(self, arguments: WeatherArguments, context: ExtensionContext):
        api_key = context.settings.google_maps_api_key.get_secret_value()
        ...
        return {"location": arguments.location, "forecast": [...]}

The platform does everything else:

  • the OpenAI tool definition: the class docstring is its description, and the arguments model’s JSON schema is its parameters;

  • parsing and validating the LLM’s arguments, and returning validation errors to the LLM;

  • the llm_tool_presented, llm_tool_requested and llm_tool_responded signals;

  • turning an exception into an {"error": ...} result, so that the LLM can recover;

  • a timeout, from the manifest’s [tool.smarter] timeout.

Raise ExtensionToolError to return a message to the LLM. Any other exception is logged with its traceback, and the LLM is told that the tool failed.

class smarter.apps.llmtool.extension.protocol.ExtensionContext(slug, version, settings, account=None, user_profile=None, llmclient=None, logger=None)[source]

Bases: object

What a tool call knows about where it runs.

Parameters:
  • slug (str) – The extension’s slug.

  • version (str) – The extension’s installed version.

  • settings (BaseModel) – The extension’s settings, one attribute per setting in its manifest. A secret setting is a pydantic.SecretStr; an unset setting is None.

  • account (Account | None) – The account of the LLMClient that called the tool, if any.

  • user_profile (UserProfile | None) – The user whose prompt called the tool, if any.

  • llmclient (object | None) – The LLMClient that called the tool, if any.

__init__(slug, version, settings, account=None, user_profile=None, llmclient=None, logger=None)
account: Account | None = None
llmclient: object | None = None
logger: Logger | WaffleSwitchedLoggerWrapper = None
settings: BaseModel
slug: str
switch_is_active(name)[source]

Return whether one of the extension’s waffle switches is on.

Parameters:

name (str) – The switch’s name in the manifest’s [[tool.smarter.switches]], without the ext_<slug>_ prefix.

Return type:

bool

Returns:

True if the switch exists and is on.

user_profile: UserProfile | None = None
version: str
smarter.apps.llmtool.extension.protocol.ExtensionLogger = logging.Logger | smarter.lib.logging.waffle_switched_logger.WaffleSwitchedLoggerWrapper

The logger of an extension’s code.

exception smarter.apps.llmtool.extension.protocol.ExtensionToolError[source]

Bases: Exception

Raised by a tool to tell the LLM why it could not do what was asked.

The message is returned to the LLM as {"error": "<message>"}, without a traceback.

class smarter.apps.llmtool.extension.protocol.SmarterExtensionTool[source]

Bases: object

The base class of a Smarter Extension’s tools.

Subclasses set name and arguments_model, document the tool in the class docstring (or set description), and implement call(). A new instance is created for each tool call.

arguments_model: ClassVar[type[BaseModel]]

The Pydantic model of the tool’s arguments.

Its JSON schema is the tool’s parameters.

call(arguments, context)[source]

Do what the LLM asked.

Parameters:
  • arguments (BaseModel) – The LLM’s arguments, validated by arguments_model.

  • context (ExtensionContext) – The extension’s settings, and who called the tool.

Return type:

TypeAliasType

Returns:

The result for the LLM: any JSON value.

Raises:

ExtensionToolError – To tell the LLM why the tool could not do what was asked.

description: ClassVar[str | None] = None

The tool’s description for the LLM.

Defaults to the class docstring.

name: ClassVar[str]

The tool’s name, unique within its extension, e.g. get_weather.

classmethod parameters()[source]

The tool’s parameters for the LLM: the JSON schema of arguments_model.

Return type:

dict[str, Any]

classmethod problems()[source]

Explain why this class is not a valid tool, if it is not.

Return type:

list[str]

Returns:

A description of each problem; empty if the tool is valid.

classmethod tool_description()[source]

The tool’s description for the LLM: description, or the class docstring.

Return type:

str

smarter.apps.llmtool.extension.protocol.extension_logger(slug)[source]

Return the logger of an extension’s code, switched by SmarterWaffleSwitches.EXTENSION_LOGGING.

Parameters:

slug (str) – The extension’s slug.

Return type:

Logger | WaffleSwitchedLoggerWrapper

smarter.apps.llmtool.extension.protocol.switch_name(slug, name)[source]

Return the waffle switch name of one of an extension’s switches.

Parameters:
  • slug (str) – The extension’s slug, e.g. date-calculator.

  • name (str) – The switch’s name in the manifest, e.g. logging.

Return type:

str

Returns:

e.g. ext_date_calculator_logging.