SAM Broker Model

The Smarter Broker Model for the command-line interface (CLI) establishes an abstract set of pattnerns for implementing a static set of commands operated on Smarter yaml manifest files. These are operations that are intended to be initiated by the Smarter CLI tool. See github.com/smarter-sh/smarter-cli.

See smarter.apps.api.v1.cli.views.base.CliBaseApiView for details on the base CLI view CliBaseApiView.

AI Resources that implement the Broker Model will subclass the AbstractBroker class (see class referencebelow).

Manifest broker.

class smarter.lib.manifest.broker.AbstractBroker(request, *args, name=None, kind=None, loader=None, api_version='smarter.sh/v1', manifest=None, file_path=None, url=None, **kwargs)[source]

Bases: ABC, SmarterRequestMixin

Abstract base class for the Smarter Broker Model.

This class defines the core broker service pattern for the Smarter API, and is the foundation for all concrete Broker implementations. Brokers are responsible for processing Smarter YAML manifests, initializing Pydantic models, and brokering the correct implementation class for CLI and API operations.

Responsibilities

  • Load, partially validate, and parse a Smarter API YAML manifest, sufficient to initialize a Pydantic model.

  • Implement the broker service pattern for the underlying object.

  • Initialize the corresponding Pydantic models.

  • Instantiate the underlying Python object for the resource.

The broker pattern provides generic services for manifest operations, including: get, post, put, delete, and patch.

Subclasses must implement the abstract methods to provide resource-specific logic for CLI and API commands such as apply, describe, delete, deploy, example_manifest, get, logs, and undeploy.

abstract property ORMMetaModelClass: Type[MetaDataWithOwnershipModel]

Return the Django ORM meta model class for the broker.

Returns:

The Django ORM meta model class definition for the broker.

Return type:

Type[MetaDataWithOwnershipModel]

abstract property ORMModelClass: Type[MetaDataWithOwnershipModel]

Return the Django ORM model class for the broker.

Returns:

The Django ORM model class definition for the broker.

Return type:

Type[MetaDataWithOwnershipModel]

property SAMModelClass: Type[AbstractSAMBase]

Return the SAM (Smarter Api Manifest) model class for the broker.

Returns:

The Pydantic model class definition for the broker.

Return type:

Type[AbstractSAMBase]

abstract property SerializerClass: Type[ModelSerializer]

Return the serializer class for the broker.

Returns:

The serializer class definition for the broker.

Return type:

Type[ModelSerializer]

__init__(request, *args, name=None, kind=None, loader=None, api_version='smarter.sh/v1', manifest=None, file_path=None, url=None, **kwargs)[source]
property abstract_broker_logger_cache_invalidation_prefix: str

Return the logger prefix for the AbstractBroker cache invalidation.

Returns:

The logger prefix for the AbstractBroker.

Return type:

str

property abstract_broker_logger_prefix: str

Return the logger prefix for the AbstractBroker.

Returns:

The logger prefix for the AbstractBroker.

Return type:

str

property abstract_broker_ready_state: str

Return a string representation of the AbstractBroker’s ready state.

Returns:

“READY” if the AbstractBroker is ready, otherwise “NOT_READY”.

Return type:

str

property api_version: str

The API version of the manifest.

Returns:

The API version of the manifest.

Return type:

Optional[str]

abstractmethod apply(request, *args, **kwargs)[source]

Apply a manifest, which works like an upsert operation.

This abstract method should be implemented by subclasses to provide the logic for applying a manifest to create or update a resource.

Designed around the Kubernetes kubectl apply command.

This method processes a Smarter YAML manifest and either creates or updates the corresponding resource, depending on whether it already exists.

Example manifest metadata:

metadata:
    description: new description
    name: test71d12b8212b628df
    version: 1.0.0
Parameters:
  • request (HttpRequest) – The HTTP request object.

  • args – Additional positional arguments.

  • kwargs – Additional keyword arguments.

Returns:

A SmarterJournaledJsonResponse if implemented, otherwise None.

Return type:

SmarterJournaledJsonResponse | None

Todo

Research why this is not an abstract method.

cache_invalidations()[source]

Handle broker specific cache invalidation logic.

Return type:

None

clean_cli_param(param, param_name='unknown', url=None)[source]
  • Remove any leading or trailing whitespace from the param.

  • Ensure that the param is a string.

  • Return the cleaned param.

Parameters:
  • param (Any) – The param to clean.

  • param_name (str) – The name of the param, for logging purposes.

  • url (str | None) – The url from which the param was extracted, for logging purposes.

Returns:

The cleaned param.

Return type:

str | None

command_result()[source]

The data of the response to a command that changed the resource, e.g. apply.

It identifies the resource, and nothing else. Use to_json() for logging only: it includes the broker’s request, authentication and cache key, which a client must not see.

Return type:

dict[str, Any]

Returns:

The resource’s api version, kind and name.

property created: bool

Return True if the broker was created successfully.

Returns:

True if the broker was created successfully.

Return type:

bool

abstractmethod delete(request, *args, **kwargs)[source]

Delete a resource.

Parameters:
  • request (HttpRequest) – The HTTP request object.

  • args – Additional positional arguments.

  • kwargs – Additional keyword arguments.

Returns:

A SmarterJournaledJsonResponse containing the result of the delete operation.

Return type:

SmarterJournaledJsonResponse

abstractmethod dependencies()[source]

Return brokers for the resources that depend on this resource.

A resource depends on this resource when it refers to it, so that deleting this resource would break it, or would delete it too. For example, an LLMClient that lists a Guardrail in its spec.guardrails depends on that Guardrail. The dependents are queried from the Django ORM, and may belong to other users. verify_no_dependencies() uses them to refuse to delete a resource that is still in use.

Example:

broker = SAMGuardrailBroker(request=request, name="stupid_input", kind="Guardrail")
for dependency in broker.dependencies():
    print(dependency.kind, dependency.name)
# LLMClient smarter_example
Returns:

A broker for each resource that depends on this resource, or an empty list.

Return type:

List[AbstractBroker]

dependencies_status()[source]

Return the status.dependencies of a manifest: the kind and name of each visible dependency.

Example:

broker.dependencies_status()
# [{"kind": "LLMClient", "name": "smarter_example"}]
Returns:

A dict with the kind and name of each dependency that the authenticated user may read.

Return type:

List[dict[str, Any]]

dependency_broker(kind, instance, name=None, user_profile=None)[source]

Return a broker for a resource that depends on this resource.

The broker is initialized from the ORM, for the resource’s own owner, which may be another user than this broker’s.

Parameters:
  • kind (str) – The SAM kind of the resource, e.g. SAMKinds.LLM_CLIENT.value.

  • instance (Model) – The resource’s Django ORM instance. It has a user_profile.

  • name (str | None) – The resource’s manifest name. Defaults to instance.name.

  • user_profile (UserProfile | None) – The resource’s owner. Defaults to instance.user_profile.

Returns:

A broker for the resource.

Return type:

AbstractBroker

dependency_brokers(kind, instances)[source]

Return a broker for each resource that depends on this resource.

Parameters:
  • kind (str) – The SAM kind of the resources, e.g. SAMKinds.LLM_CLIENT.value.

  • instances (Iterable[Model]) – The resources’ Django ORM instances.

Returns:

A broker for each resource, without duplicates.

Return type:

List[AbstractBroker]

abstractmethod deploy(request, *args, **kwargs)[source]

Deploy a resource.

Parameters:
  • request (HttpRequest) – The HTTP request object.

  • args – Additional positional arguments.

  • kwargs – Additional keyword arguments.

Returns:

A SmarterJournaledJsonResponse containing the result of the deploy operation.

Return type:

SmarterJournaledJsonResponse

deployable: ClassVar[bool] = False

True when the broker implements deploy().

The cli api’s resources command reports it as each kind’s Deployable, and the smarter cli offers deploy and undeploy commands for those kinds. A broker that sets it must override deploy(); undeploy() may still raise SAMBrokerErrorNotImplemented, as CustomDomain’s and LLMHostCompute’s do.

abstractmethod describe(request, *args, **kwargs)[source]

Describe a resource.

Parameters:
  • request (HttpRequest) – The HTTP request object.

  • args – Additional positional arguments.

  • kwargs – Additional keyword arguments.

Returns:

A SmarterJournaledJsonResponse containing the description of the resource.

Return type:

SmarterJournaledJsonResponse

abstractmethod example_manifest(request, *args, **kwargs)[source]

Returns an example yaml manifest document for the kind of resource.

Parameters:
  • request (HttpRequest) – The HTTP request object.

  • args – Additional positional arguments.

  • kwargs – Additional keyword arguments.

Returns:

A SmarterJournaledJsonResponse containing the example manifest.

Return type:

SmarterJournaledJsonResponse

property formatted_class_name: str

Return the logger prefix for the AbstractBroker.

Returns:

The logger prefix for the AbstractBroker.

Return type:

str

property formatted_class_name_cache_invalidations: str

Return the logger prefix for the AbstractBroker cache invalidations.

Returns:

The logger prefix for the AbstractBroker cache invalidations.

Return type:

str

abstractmethod get(request, *args, **kwargs)[source]

Get information about specified resources.

Parameters:
  • request (HttpRequest) – The HTTP request object.

  • args – Additional positional arguments.

  • kwargs – Additional keyword arguments.

Returns:

A SmarterJournaledJsonResponse containing the result of the get operation.

Return type:

SmarterJournaledJsonResponse

get_model_titles(serializer)[source]

For tabular output from get() implementations.

Returns a list of field names and types from the Django model serializer.

Parameters:

serializer (ModelSerializer) – The Django model serializer instance.

Returns:

A list of field names and types.

Return type:

list[dict[str, str]] | None

get_or_create_secret(user_profile, name, value=None, description=None, expiration=None)[source]

Get or create a Smarter Secret in the database.

This is used to store secrets that are passed in the manifest.

Parameters:
  • user_profile (UserProfile) – The UserProfile to associate the secret with.

  • name (str) – The name of the secret.

  • value (str | None) – The value of the secret.

  • description (str | None) – A description of the secret.

  • expiration (datetime | None) – The expiration date of the secret.

Returns:

The created or retrieved Secret object.

Return type:

Secret

property is_ready_abstract_broker: bool

Return True if the AbstractBroker is ready for operations.

An AbstractBroker is considered ready if: - The AccountMixin is ready. - The RequestMixin is ready. - either a valid manifest is loaded or a ready SAMLoader is present.

Returns:

True if the AbstractBroker is ready for operations.

Return type:

bool

property is_valid: bool
json_response_err(command, e)[source]

Return a structured error response that can be unpacked and rendered by the cli in a variety of formats.

Parameters:
Returns:

A SmarterJournaledJsonResponse containing the error response.

Return type:

SmarterJournaledJsonResponse

json_response_err_notfound(command, message=None)[source]

Return a common not found response.

Parameters:
Returns:

A SmarterJournaledJsonResponse containing the not found response.

Return type:

SmarterJournaledJsonResponse

json_response_err_notimplemented(command)[source]

Return a common not implemented response.

Parameters:

command (SmarterJournalCliCommands) – The command that was executed.

Returns:

A SmarterJournaledJsonResponse containing the not implemented response.

Return type:

SmarterJournaledJsonResponse

json_response_err_notready(command)[source]

Return a common not ready response.

Parameters:

command (SmarterJournalCliCommands) – The command that was executed.

Returns:

A SmarterJournaledJsonResponse containing the not ready response.

Return type:

SmarterJournaledJsonResponse

json_response_err_readonly(command)[source]

Return a common read-only response.

Parameters:

command (SmarterJournalCliCommands) – The command that was executed.

Returns:

A SmarterJournaledJsonResponse containing the read-only response.

Return type:

SmarterJournaledJsonResponse

json_response_ok(command, data=None, message=None)[source]

Return a common success response.

Parameters:
Returns:

A SmarterJournaledJsonResponse containing the success response.

Return type:

SmarterJournaledJsonResponse

property kind: str | None

The kind of manifest.

Returns:

The kind of manifest.

Return type:

Optional[str]

kind_setter(value)[source]

Set the kind of manifest.

Validates that the kind is a valid SmarterJournalThings value.

Raises:

SmarterValueError – If the kind is not valid.

Parameters:

value (str) – The kind of manifest to set.

property loader: SAMLoader | None

The SAMLoader instance for this broker.

Returns:

The SAMLoader instance for this broker.

Return type:

Optional[SAMLoader]

log_abstract_broker_state()[source]

Log the current state of the AbstractBroker instance for debugging purposes.

Returns:

None

abstractmethod logs(request, *args, **kwargs)[source]

Get logs for a resource.

Parameters:
  • request (HttpRequest) – The HTTP request object.

  • args – Additional positional arguments.

  • kwargs – Additional keyword arguments.

Returns:

A SmarterJournaledJsonResponse containing the logs for the resource.

Return type:

SmarterJournaledJsonResponse

abstract property manifest: dict[str, Any] | AbstractSAMBase | None

The Pydantic model representing the manifest.

If the manifest has not been initialized yet, this property will attempt to initialize it using the SAMLoader.

Returns:

The Pydantic model representing the manifest.

Return type:

Optional[AbstractSAMBase]

manifest_setter(value)[source]

Set the manifest for the broker and override all AbstractBroker model properties based on the manifest data.

Parameters:

value (dict[str, Any] | AbstractSAMBase | None) – The manifest to set, either as a Pydantic model or a dictionary.

manifest_to_django_orm()[source]

Convert the Smarter API manifest metadata into a dictionary suitable for creating or updating a Django ORM LLMClient model.

This method extracts all relevant metadata from the loaded manifest and transforms it into a dictionary format compatible with Django ORM operations. The manifest’s configuration is first dumped and converted from camelCase to snake_case to match Django’s field naming conventions.

The resulting dictionary includes the account, name, description, and version fields from the manifest metadata. This dictionary is intended to be used to supplement the model spec when instantiating or updating a LLMClient ORM model instance in the database.

If the manifest is not loaded or is invalid, an exception is raised to indicate that the broker is not ready to perform the transformation.

Returns:

A dictionary containing all metadata fields required to create or update a Django ORM LLMClient model.

Return type:

dict[str, Any]

Raises:
  • SAMBrokerErrorNotReady – If the manifest is not loaded or cannot be found.

  • SAMLLMClientBrokerError – If the manifest metadata cannot be converted to a dictionary.

property name: str | None

Retrieve the unique name identifier for the LLMClient instance managed by this broker.

This property accesses the name used to distinguish the LLMClient within the database and across the Smarter platform. The name is first returned from an internal cache if available. If not cached, and if a manifest is present, the name is extracted from the manifest’s metadata and stored for subsequent access.

The name is essential for database queries, model lookups, and for associating related resources such as API keys, plugins, and functions with the correct LLMClient instance.

Returns:

The name of the LLMClient as a string, or None if the name is not set or cannot be determined.

Return type:

Optional[str]

Note

The name property is a critical identifier used throughout the broker to ensure correct mapping between manifest data and persistent application state.

name_cached_property_setter(value)[source]

A workaround to the limitation that you cannot use both @cached_property and a setter for the same attribute name (name).

In Python, you cannot have a property (or cached_property) and a setter with the same name unless you use the @property decorator (not @cached_property).

We need the cached_property so that the lazy evaluation of the name only happens once, and subsequent accesses return the cached value for performance. However, we also need to be able to set the name explicitly in some cases,

Parameters:

value (str) – The name to set for the manifest.

property orm_instance: MetaDataWithOwnershipModel | None

Return the Django ORM model instance for the broker.

There are multiple strategies to retrieve the ORM instance:

  1. If the instance is already cached in self._orm_instance, return it.

  2. If the broker is not ready or the name is not set, log a warning and return None.

  3. Attempt to retrieve the ORM instance using the user_profile and name. If not found, attempt to retrieve using the admin user_profile for the account. If still not found, attempt to retrieve using the Smarter platform admin user_profile.

  4. Cache the retrieved instance for future access.

Returns:

The Django ORM model instance for the broker.

Return type:

Optional[MetaDataWithOwnershipModel]

property orm_meta_instance: MetaDataWithOwnershipModel | None

Return the Django ORM meta model instance for the broker.

This is a cached property that retrieves the ORM meta instance based on the user_profile and kind. For simple relational models, the ORM meta class is the same as the ORM class, and the meta instance is the same as the ORM instance.

This property is used for resolving more complex ORM relationships where the name and user_profile fields are stored in a parent Django model.

orm_meta_instance_setter()[source]

Initialize the ORM metadata for the broker instance.

This method attempts to initialize the ORM metadata by querying the ORMMetaModelClass using the broker’s name and user_profile. If the ORM metadata is successfully retrieved, it is stored in the orm_meta_instance attribute. If the ORM metadata does not exist or an error occurs, the _orm_instance attribute is set to None.

Return type:

None

Returns:

None

property params: QueryDict | None

Return the query parameters from the url of the request.

There are two scenarios to consider:

  1. the request is a Django HttpRequest object (the expected case)

  2. the request is a Python PreparedRequest object (the edge case)

Returns:

The query parameters from the url of the request.

Return type:

Optional[QueryDict]

abstractmethod prompt(request, *args, **kwargs)[source]

Invoke a prompt operation.

This abstract method should be implemented by subclasses to provide prompt-based interactions with the broker resource.

Parameters:
  • request (HttpRequest) – The HTTP request object.

  • args – Additional positional arguments.

  • kwargs – Additional keyword arguments.

Returns:

A SmarterJournaledJsonResponse containing the prompt response.

Return type:

SmarterJournaledJsonResponse

raise_for_unknown_keys(manifest)[source]

Raise a validation error if the manifest has keys that its Pydantic model does not define.

The models ignore such keys, so that a misspelled key, e.g. defaultTemperatura, would otherwise be silently ignored, rather than be reported to the client as an error.

Return type:

None

readable_by_name(model_class, owned)[source]

Return the resource to describe: the user’s own, or else one with this name that the user may read.

get lists every resource that the user may read, including the ones that are shared with the account, e.g. built-in Guardrails. describe must find each of them by the name that get returned. apply and delete must not: they act on the user’s own resource only.

Parameters:
Return type:

MetaDataWithOwnershipModel | None

Returns:

The resource, or None if the user may read none of this name.

property ready: bool

Return True if the broker is ready for operations.

A broker is considered ready if it has a valid manifest loaded.

Returns:

True if the broker is ready for operations.

Return type:

bool

property ready_state: str

Return a string representation of the broker’s ready state.

Returns:

“READY” if the broker is ready, otherwise “NOT_READY”.

Return type:

str

property request: HttpRequest | None

Return the request object.

Returns:

The request object.

Return type:

Optional[HttpRequest]

schema(request, *args, **kwargs)[source]

Return the published JSON schema for the Pydantic model.

Parameters:
  • request (HttpRequest) – The HTTP request object.

  • args – Additional positional arguments.

  • kwargs – Additional keyword arguments.

Returns:

A SmarterJournaledJsonResponse containing the JSON schema.

Return type:

SmarterJournaledJsonResponse

set_and_verify_name_param(*args, command=None, **kwargs)[source]

Set self.name from the ‘name’ query string param and then verify that it was actually passed.

Parameters:

command (SmarterJournalCliCommands | None) – The command being executed, for error reporting purposes.

Raises:

SAMBrokerErrorNotReady – If neither a manifest nor a name param is provided.

Returns:

None

property thing: SmarterJournalThings

The Smarter Journal Thing for this broker.

Returns:

The Smarter Journal Thing for this broker.

Return type:

SmarterJournalThings, an enumeration of all Smarter AI resource types.

to_json()[source]

Serialize the broker instance to a JSON string, for logging.

It includes the broker’s internal state, e.g. its request, authentication and cache key, so it is never returned to a client. See command_result().

Returns:

A JSON string representation of the broker instance.

Return type:

dict[str, Any]

abstractmethod undeploy(request, *args, **kwargs)[source]

Undeploy a resource.

Parameters:
  • request (HttpRequest) – The HTTP request object.

  • args – Additional positional arguments.

  • kwargs – Additional keyword arguments.

Returns:

A SmarterJournaledJsonResponse containing the result of the undeploy operation.

Return type:

SmarterJournaledJsonResponse

property uri: str | None

Return the full uri of the request.

Returns:

The full uri of the request.

Return type:

Optional[str]

classmethod validation_errors(manifest)[source]

Validate a manifest against the broker’s Pydantic model, without saving it.

Pydantic’s errors keep their loc, the path of the invalid value in the manifest. Errors that the model’s validators raise as other exceptions, e.g. SAMValidationError, have an empty loc.

Example:

SAMGuardrailBroker.validation_errors({"apiVersion": "smarter.sh/v1", "kind": "Guardrail"})
# [{"loc": ["metadata"], "message": "Field required", "type": "missing"}, ...]
Parameters:

manifest (dict[str, Any]) – The manifest, as a dict.

Returns:

The manifest’s errors, as dicts with a loc, a message and a type, or an empty list.

Return type:

list[dict[str, Any]]

verify_no_dependencies(command=None)[source]

Raise an error if other resources depend on this resource.

Call this in delete(), before deleting anything.

Parameters:

command (SmarterJournalCliCommands | None) – The command that is being processed.

Raises:

SAMBrokerErrorDependencies – If dependencies() returns any brokers.

Return type:

None

visible_dependencies(dependencies=None)[source]

Return the dependencies that the authenticated user may read.

A dependency may belong to another account, e.g. an LLMClient that uses a built-in Guardrail. Superusers may read every dependency. Other users may read those of their own account.

Parameters:

dependencies (List[AbstractBroker] | None) – The dependencies to filter. Defaults to dependencies().

Returns:

The dependencies that the authenticated user may read.

Return type:

List[AbstractBroker]

exception smarter.lib.manifest.broker.SAMBrokerError(message=None, thing=None, command=None, stack_trace=None)[source]

Bases: SAMExceptionBase

Base class for all SAMBroker errors.

__init__(message=None, thing=None, command=None, stack_trace=None)[source]
command: SmarterJournalCliCommands | None = None
property get_formatted_err_message
stack_trace: str | None = None
thing: SmarterJournalThings | str | None = None
exception smarter.lib.manifest.broker.SAMBrokerErrorDependencies(message=None, thing=None, command=None, stack_trace=None)[source]

Bases: SAMBrokerError

Error for deleting a resource that other resources depend on.

property get_formatted_err_message
exception smarter.lib.manifest.broker.SAMBrokerErrorNotFound(message=None, thing=None, command=None, stack_trace=None)[source]

Bases: SAMBrokerError

Error for broker operations on resources that are not found.

property get_formatted_err_message
exception smarter.lib.manifest.broker.SAMBrokerErrorNotImplemented(message=None, thing=None, command=None, stack_trace=None)[source]

Bases: SAMBrokerError

Base class for all SAMBroker errors.

property get_formatted_err_message
exception smarter.lib.manifest.broker.SAMBrokerErrorNotReady(message=None, thing=None, command=None, stack_trace=None)[source]

Bases: SAMBrokerError

Error for broker operations on resources that are not ready.

property get_formatted_err_message
exception smarter.lib.manifest.broker.SAMBrokerInternalError(message=None, thing=None, command=None, stack_trace=None)[source]

Bases: SAMBrokerError

Error for broker operations that result in an internal error, such as trying to create a resource that already exists.

property get_formatted_err_message
exception smarter.lib.manifest.broker.SAMBrokerReadOnlyError(message=None, thing=None, command=None, stack_trace=None)[source]

Bases: SAMBrokerError

Error for read-only broker operations.

property get_formatted_err_message
smarter.lib.manifest.broker.memoized_dependencies(func)[source]

Memoize a broker’s AbstractBroker.dependencies() for the life of the broker instance.

The dependencies are queried, and their brokers built, once per broker instance. AbstractBroker.verify_no_dependencies() clears the memo before it checks, so that a delete always sees the current dependencies.

Example:

class SAMGuardrailBroker(AbstractBroker):

    @memoized_dependencies
    def dependencies(self) -> List[AbstractBroker]:
        ...
Parameters:

func (Callable[..., List[AbstractBroker]]) – The broker’s dependencies() method.

Returns:

The memoized method.

Return type:

Callable[..., List[AbstractBroker]]