pyproject.toml

A Smarter Extension’s pyproject.toml, at the root of its archive.

It is not a SAM manifest: it is a standard Python project file, which this module calls the extension’s manifest only in its class names.

An Extension describes itself with a standard pyproject.toml. The [project] table holds what any Python package declares (name, version, description, authors, license, dependencies), and the [tool.smarter] table holds what Smarter needs: the extension’s slug, its Python package, the tools it provides, the settings an administrator must configure, and the Smarter versions it is compatible with.

[project]
name = "smarter-weather"
version = "1.0.0"
description = "Current weather and a 24-hour forecast for any location."
authors = [{ name = "Smarter Team" }]
license = "AGPL-3.0"
requires-python = ">=3.12"
dependencies = ["googlemaps", "openmeteo-requests>=1.5"]

[tool.smarter]
slug = "weather"
package = "weather"
requires-smarter = ">=0.27"
tools = ["weather.tools:GetWeather"]

[[tool.smarter.settings]]
name = "google_maps_api_key"
type = "secret"
required = true
description = "Google Maps Geocoding API key."

Dependencies are checked, never installed: an Extension may import only what the platform already ships. ExtensionManifest.compatibility_problems() explains every unmet requirement.

class smarter.apps.llmtool.extension.manifest.ExtensionAuthor(**data)[source]

Bases: BaseModel

An author in [project].authors.

email: str | None
model_config = {'extra': 'ignore'}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

name: str | None
class smarter.apps.llmtool.extension.manifest.ExtensionManifest(**data)[source]

Bases: BaseModel

A Smarter Extension’s manifest, parsed from its pyproject.toml.

Example usage:

manifest = ExtensionManifest.from_pyproject(text)
problems = manifest.compatibility_problems()
compatibility_problems(platform_version='9999.9999.9999.dev9999')[source]

Explain why this extension cannot run on this platform, if it cannot.

Checks the Python version, the Smarter version, and that every dependency is installed at a version that satisfies it. An unreleased Smarter build satisfies every requires-smarter.

Parameters:

platform_version (str) – The Smarter version to check against.

Return type:

list[str]

Returns:

A description of each unmet requirement; empty if the extension is compatible.

property display_name: str

display-name, or the slug in title case.

Type:

The name shown to people

classmethod from_pyproject(text)[source]

Parse and validate the text of a pyproject.toml.

Parameters:

text (str) – The file’s text.

Return type:

ExtensionManifest

Returns:

The manifest.

Raises:

SmarterExtensionManifestError – If the file is not valid TOML, or not a valid extension manifest.

model_config = {'extra': 'forbid'}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

project: ExtensionProject
setting(name)[source]

Return the declared setting named name, or None.

Return type:

ExtensionSettingSpec | None

property slug: str

The extension’s slug.

smarter: ExtensionSmarterTable
property version: str

The extension’s version, as written in the manifest.

class smarter.apps.llmtool.extension.manifest.ExtensionProject(**data)[source]

Bases: BaseModel

The [project] table of an extension’s pyproject.toml.

Extra fields are allowed, because [project] is a Python packaging standard that defines more fields than Smarter reads.

authors: list[ExtensionAuthor]
dependencies: list[str]
description: str
license: str | None
model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

name: str
requires_python: str | None
urls: dict[str, str]
classmethod validate_dependencies(v)[source]

Each dependency must be a PEP 508 requirement.

Return type:

list[str]

classmethod validate_license(v)[source]

Accept an SPDX expression (PEP 639), or the older {text = ...} and {file = ...} tables.

Return type:

str | None

classmethod validate_version(v)[source]

The version must be a PEP 440 version, so that versions can be compared.

Return type:

str

version: str
class smarter.apps.llmtool.extension.manifest.ExtensionSettingSpec(**data)[source]

Bases: BaseModel

A setting that an Extension declares, and that an administrator configures.

A secret setting’s value is a Secret, so it is encrypted at rest, and reaches the extension’s code as a pydantic.SecretStr.

choices: list[str] | None
coerce(value)[source]

Convert a configured value, which may be text from a form, to the setting’s type.

Parameters:

value (str | int | float | bool) – The value.

Return type:

str | int | float | bool

Returns:

The value, as the setting’s type.

Raises:

ValueError – If the value cannot be converted.

default: str | int | float | bool | None
description: str
model_config = {'extra': 'forbid'}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

name: str
required: bool
type: ExtensionSettingType
classmethod validate_name(v)[source]

A setting’s name must be a Python identifier, so that the extension can read it as an attribute.

Return type:

str

validate_type()[source]

A choice setting needs choices, a secret has no default, and a default must have the setting’s type.

Return type:

ExtensionSettingSpec

class smarter.apps.llmtool.extension.manifest.ExtensionSettingType(*values)[source]

Bases: StrEnum

The type of an extension setting, which determines its form field and its Python type.

BOOLEAN = 'boolean'
CHOICE = 'choice'
FLOAT = 'float'
INTEGER = 'integer'
SECRET = 'secret'
STRING = 'string'
URL = 'url'
class smarter.apps.llmtool.extension.manifest.ExtensionSmarterTable(**data)[source]

Bases: BaseModel

The [tool.smarter] table of an extension’s pyproject.toml.

display_name: str | None
icon: str | None
lifecycle: str | None
model_config = {'extra': 'forbid', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

package: str
requires_smarter: str | None
settings: list[ExtensionSettingSpec]
slug: str
switches: list[ExtensionSwitchSpec]
timeout: int
tools: list[str]
classmethod validate_package(v)[source]

The package must be a top-level Python package name.

Return type:

str

classmethod validate_requires_smarter(v)[source]

requires-smarter must be a PEP 440 version specifier.

Return type:

str | None

classmethod validate_slug(v)[source]

A slug is lowercase words separated by hyphens, so that it is safe in URLs, paths and function names.

Return type:

str

validate_tools_and_settings()[source]

Every tool must be in the extension’s package, and setting names must be unique.

Return type:

ExtensionSmarterTable

class smarter.apps.llmtool.extension.manifest.ExtensionSwitchSpec(**data)[source]

Bases: BaseModel

A waffle switch that an Extension owns.

It is created, as ext_<slug>_<name>, when the extension is installed, and deleted when it is uninstalled. The extension’s code reads it with switch_is_active().

default: bool
description: str
model_config = {'extra': 'forbid'}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

name: str
classmethod validate_name(v)[source]

A switch’s name is lowercase letters, digits and underscores.

Return type:

str

smarter.apps.llmtool.extension.manifest.SettingValue = str | int | float | bool

The value of an extension setting.

smarter.apps.llmtool.extension.manifest.is_development_version(version)[source]

Return whether a Smarter version is an unreleased build, e.g. 9999.9999.9999.dev9999.

Parameters:

version (str) – The version.

Return type:

bool

Returns:

True for an unreleased build.