Base Class
A Compound Model class for managing plugins.
- class smarter.apps.plugin.plugin.base.PluginBase(*args, user_profile=None, selected=False, api_version=None, manifest=None, plugin_id=None, plugin_meta=None, data=None, name=None, **kwargs)[source]
Bases:
ABC,AccountMixin,SmarterConverterMixinAbstract base class for Smarter plugins.
This class provides a comprehensive framework for managing plugin lifecycle, including creation, updating, deletion, cloning, and serialization. It integrates with Django ORM models and serializers, and supports manifest-based initialization using Pydantic models or YAML/JSON representations.
Initialization Options:
Via Pydantic model from a manifest broker (preferred).
By Django model plugin ID.
From YAML or JSON manifest representations.
Responsibilities
Manages plugin metadata, selector, prompt, and data models.
Handles serialization to JSON and YAML formats compatible with Smarter API.
Supports OpenAI function calling schema for plugin tools.
Provides hooks for plugin selection logic and prompt customization.
Ensures validation and readiness of plugin components.
Integrates with Django signals for plugin lifecycle events.
Usage
Subclass this base and implement all abstract properties and methods.
Use provided factory and utility methods for parameter and data conversion.
Leverage transaction management for safe database operations.
Notes
All subclasses must define the plugin data class, serializer, and manifest handling.
This class expects a valid UserProfile for most operations.
Exceptions are raised for misconfiguration or invalid states.
See the Smarter API documentation for details on plugin manifest structure and lifecycle.
- SAMPluginType
alias of
SAMPluginCommon
- __init__(*args, user_profile=None, selected=False, api_version=None, manifest=None, plugin_id=None, plugin_meta=None, data=None, name=None, **kwargs)[source]
Initialize a PluginBase instance.
Key features:
Lazy instantiation of class properties.
Supports multiple initialization methods including manifest, database model, and YAML/JSON.
Integrates with Django ORM for plugin metadata management.
Utilizes Pydantic models for manifest validation and serialization.
Provides hooks for plugin selection and prompt customization.
Ensures plugin readiness and validation before use.
Sends signals upon plugin creation or update.
This constructor supports several ways to create a plugin object:
Manifest-based initialization:
Pass a Pydantic model instance via the
manifestargument. This is the preferred method and is typically used when loading plugins from a manifest broker.Database model initialization:
Provide a Django model plugin ID via
plugin_idor aPluginMetainstance viaplugin_metato load an existing plugin from the database.YAML/JSON manifest initialization:
Supply a YAML or JSON string (or dictionary) via the
dataargument to create a plugin from a manifest representation.
See
./data/sample-plugins/everlasting-gobstopper.yamlfor an example manifest.The constructor sets up internal state, initializes plugin properties, and triggers plugin creation or update logic as needed. It also sends signals when the plugin is ready.
- Parameters:
user_profile (Optional[UserProfile]) – The user profile associated with the plugin.
selected (bool) – Whether the plugin is initially selected.
api_version (Optional[str]) – The API version for the plugin manifest.
manifest (Optional[SAMPluginCommon]) – A Pydantic model representing the plugin manifest.
plugin_id (Optional[int]) – The Django ORM plugin ID.
plugin_meta (Optional[PluginMeta]) – The Django ORM PluginMeta instance.
data (Union[dict[str, Any], str, None]) – YAML/JSON manifest as a string or dictionary.
name (Optional[str]) – The name of the plugin.
kwargs – Additional keyword arguments.
- property account: Account | None
Returns the account for the current user. Handle lazy instantiation from user or user_profile.
- Returns:
The account for the current user.
- Return type:
Account or None
- property account_number: str | None
A helper function to get the account number from the account.
- Returns:
The account number for the current account.
- Return type:
str or None
- property accountmixin_ready_state: str
Returns a string representation of the AccountMixin ready state.
- Returns:
String representation of the AccountMixin ready state.
- Return type:
- property amnesty_urls: list[str]
Returns a list of URLs that are exempt from certain checks.
This is a placeholder and should be overridden in subclasses.
- property api_version: str
Return the api version of the plugin.
- Returns:
The api version of the plugin.
- Return type:
- Raises:
SAMValidationError – If the api version is not valid.
See also
smarter.common.api.SmarterApiVersions
SMARTER_API_MANIFEST_COMPATIBILITY
- authenticate(api_token)
Authenticate the user using the provided API token. The api_token will have been generated by the SmarterTokenAuthentication class and passed by the caller in the Authorization header of the request.
Example
Authorization: Token <api_token>
- cache_invalidations()[source]
Invalidate cached properties related to the plugin.
This method is used to clear any cached data associated with the plugin instance, such as cached Django ORM model instances or serializers. It ensures that subsequent accesses to these properties will fetch fresh data from the database or re-instantiate serializers as needed.
The following properties are invalidated:
user_profileplugin_metaplugin_selectorplugin_promptplugin_data
- Return type:
- camel_to_snake(data)
Converts camelCase strings, dictionary keys, or lists of such, to snake_case format.
- Parameters:
data (str, dict, or list) – The input to convert. Can be a string, a dictionary (with camelCase keys), or a list containing strings or dictionaries.
- Returns:
The converted data in snake_case format. Returns a string, dictionary, or list, matching the input type.
- Return type:
Note
For dictionaries, only keys are converted. Values are preserved as-is, except for nested dictionaries, which are also converted.
Spaces in keys are replaced with underscores.
Multiple consecutive underscores are collapsed into a single underscore.
Nested dictionaries and lists are processed recursively.
Warning
If the input is not a string, dictionary, or list, a
SmarterValueErroris raised.Example usage:
from smarter.common.utils import camel_to_snake # Convert a string print(camel_to_snake("userName")) # Output: user_name # Convert a dictionary data = { "userName": "alice", "userProfile": { "firstName": "Alice", "lastName": "Smith" } } print(camel_to_snake(data)) # Output: {'user_name': 'alice', 'user_profile': {'first_name': 'Alice', 'last_name': 'Smith'}} # Convert a list of strings print(camel_to_snake(["firstName", "lastName"])) # Output: ['first_name', 'last_name']
- clone(new_name=None)[source]
Clone a plugin.
- Parameters:
new_name (Optional[str]) – The new name for the cloned plugin. If None, a name will be generated.
- Returns:
The id of the cloned plugin if successful, False otherwise.
- Return type:
Optional[int]
- Raises:
SmarterPluginError – If the plugin is not ready.
- create()[source]
Create a plugin from either yaml or a dictionary.
- Returns:
True if the plugin was created successfully, False otherwise.
- Return type:
- Raises:
SmarterPluginError – If the plugin manifest is not set.
- property custom_tool: dict[str, Any][source]
Return the plugin tool definition for OpenAI function calling.
See the OpenAI documentation: https://platform.openai.com/docs/assistants/tools/function-calling/quickstart
Example:
tools = [ { "type": "function", "function": { "name": "get_current_temperature", "description": "Get the current temperature for a specific location", "parameters": { "type": "object", "properties": { "location": { "type": "string", "description": "The city and state, e.g., San Francisco, CA" }, "unit": { "type": "string", "enum": ["Celsius", "Fahrenheit"], "description": "The temperature unit to use. Infer this from the user's location." } }, "required": ["location", "unit"] } } } ]
See also
smarter.lib.openai.enum.OpenAIToolCallsmarter.lib.openai.enum.OpenAIToolTypes
- property data: dict | None
Return the plugin as a dictionary.
- Returns:
The plugin as a dictionary if ready, None otherwise.
- Return type:
Optional[dict]
- data_to_dict(data)
Converts data to a dictionary, handling different types of input.
This method accepts either a dictionary or a string. If a string is provided, it will attempt to parse it as JSON first, and if that fails, as YAML. If parsing fails or the data type is unsupported, a SmarterValueError is raised.
- delete()[source]
Delete a plugin.
- Returns:
True if the plugin was deleted successfully, False otherwise.
- Return type:
- Raises:
SmarterPluginError – If the plugin is not ready.
- classmethod example_manifest(kwargs=None)[source]
Return an example manifest for the plugin. Must be implemented by subclasses.
- property formatted_class_name: str
Returns the class name in a formatted string along with the name of this mixin.
- property formatted_pluginbase_class_name: str
Return the formatted class name for logging.
- Returns:
The formatted class name.
- Return type:
- property formatted_state_not_ready: str
Returns the not-ready state formatted for logging.
- Returns:
The formatted not-ready state as a string.
- Return type:
- property formatted_state_ready: str
Returns the readiness state formatted for logging.
- Returns:
The formatted readiness state as a string.
- Return type:
- property function_calling_identifier: str
Return the function calling plugin.
- Returns:
The function calling plugin identifier.
- Return type:
- Raises:
SmarterPluginError – If the plugin is not ready.
- property function_parameters: dict[str, Any] | None
Fetch the function parameters from the Django model. Return the function parameters in a dictionary formatted according to the OpenAI function calling schema.
- property id: int | None
Return the Django ORM PluginMeta id.
- Returns:
The id of the plugin.
- Return type:
Optional[int]
- property is_accountmixin_ready: bool
Returns True if the AccountMixin is ready to be used. This is a convenience property that checks if the account and user are initialized. AccountMixin is considered ready if: - self.user is an instance of User - self.user_profile is an instance of UserProfile - self.account is an instance of Account
- Returns:
True if the AccountMixin is ready to be used.
- Return type:
- property is_authenticated: bool
Returns True if the user is authenticated and is associated with an Account.
- property kind: str
Return the kind of the plugin.
- Returns:
The kind of the plugin.
- Return type:
See also
smarter.apps.plugin.manifest.models.static_plugin.const.MANIFEST_KIND
- Example:
`python foo = MyPlugin() print(foo.kind) "SqlPlugin" `
- log_account_mixin_ready_status()
Logs the ready status of the AccountMixin.
- property manifest: SAMPluginCommon
Return the Pydandic model of the plugin.
- Returns:
The Pydantic model of the plugin.
- Return type:
- mask_string(string='', mask_char='*', mask_length=4, string_length=8)
Masks a string for secure logging.
This utility function masks all but the last unmasked_chars characters of the input string, replacing them with asterisks. It is useful for logging sensitive information like API keys or passwords.
- property metadata_class: str | None
Return the metadata class, PluginMeta.plugin_class
- Returns:
The metadata class name.
- Return type:
Optional[str]
See also:
smarter.apps.plugin.models.PluginMeta
- property name: str | None
Return the name of the plugin.
- Returns:
The name of the plugin.
- Return type:
Optional[str]
- Raises:
SmarterPluginError – If the PluginMeta is not of the expected type.
- classmethod parameter_factory(name, data_type, description, enum=None, required=False, default=None)[source]
Factory method to create a parameter dictionary for the SQL plugin.
- Parameters:
name (str) – The name of the parameter.
data_type (str) – The data type of the parameter.
description (str) – The description of the parameter.
enum (Optional[list]) – An optional list of enumerated values for the parameter.
required (Optional[bool]) – Whether the parameter is required. Default is False.
default (Optional[Any]) – The default value of the parameter. Default is None.
- Returns:
A dictionary representing the parameter.
- Return type:
- pascal_to_snake(name)
Converts a PascalCase string to pascal_case snake_case format.
- Parameters:
name (str) – The PascalCase string to convert.
- Returns:
The converted string in snake_case format.
- Return type:
Note
Spaces in the input string are replaced with underscores.
Multiple consecutive underscores are collapsed into a single underscore.
Example usage:
from smarter.common.utils import pascal_to_snake print(pascal_to_snake("UserProfile")) # Output: user_profile print(pascal_to_snake("FirstName LastName")) # Output: first_name_last_name
- abstract property plugin_data: PluginDataBase
Return the plugin data as a Django ORM instance.
This abstract property must be implemented by all subclasses of
PluginBase. It should return an instance of the Django model that represents the plugin’s data. This instance is used for accessing and manipulating plugin-specific data stored in the database.- Returns:
The Django ORM model instance for plugin data.
- Return type:
- Raises:
NotImplementedError – If not implemented in a subclass.
- abstract property plugin_data_class: type[PluginDataBase]
Return the Django ORM base class for plugin data.
This abstract property must be implemented by all subclasses. It should return the Django model class that represents the plugin’s data structure. This model is used for storing and retrieving plugin-specific data from the database.
Subclasses should ensure that the returned class inherits from
PluginDataBaseand defines all necessary fields for the plugin’s operation.- Returns:
The Django ORM model class for plugin data.
- Return type:
- Raises:
NotImplementedError – If not implemented in a subclass.
Example:
from smarter.apps.plugin.plugin.base import PluginDataBase class MyPluginData(PluginDataBase): # Define fields specific to MyPluginData pass foo = MyPlugin() assert foo.plugin_data_class == MyPluginData
- abstract property plugin_data_django_model: dict
Return the plugin data definition as a json object.
- Returns:
The plugin data definition as a dictionary.
- Return type:
- Raises:
NotImplementedError – If not implemented in a subclass.
- abstract property plugin_data_serializer: ModelSerializer
Return the plugin data serializer for the plugin data Django ORM.
- Returns:
The serializer instance for the plugin data.
- Return type:
serializers.ModelSerializer
- Raises:
NotImplementedError – If not implemented in a subclass.
- abstract property plugin_data_serializer_class: type[ModelSerializer]
Return the plugin data serializer class for the plugin data Django ORM.
- Returns:
The serializer class for the plugin data.
- Return type:
type[serializers.ModelSerializer]
- Raises:
NotImplementedError – If not implemented in a subclass.
- property plugin_meta: PluginMeta | None
Return the plugin meta.
- Returns:
The plugin meta.
- Return type:
Optional[PluginMeta]
Note
This property will attempt to load the PluginMeta from the database if it has not already been set and if the UserProfile and manifest are available.
- property plugin_meta_django_model: dict[str, Any] | None
Return a dict for loading the plugin meta Django ORM model.
- property plugin_meta_serializer: PluginMetaSerializer | None
Return the plugin meta serializer.
- Returns:
The plugin meta serializer.
- Return type:
Optional[PluginMetaSerializer]
- property plugin_prompt: PluginPrompt | None
Return the plugin prompt.
- Returns:
The plugin prompt.
- Return type:
- Raises:
SmarterPluginError – If the PluginPrompt does not exist.
- property plugin_prompt_django_model: dict[str, Any] | None
Return the plugin prompt definition as a json object that can be used to load the Django ORM model.
- property plugin_prompt_serializer: PluginPromptSerializer | None
Return the plugin prompt serializer.
- Returns:
The plugin prompt serializer.
- Return type:
Optional[PluginPromptSerializer]
- property plugin_selector: PluginSelector | None
Return the plugin selector.
- Returns:
The plugin selector.
- Return type:
Optional[PluginSelector]
- property plugin_selector_django_model: dict[str, Any] | None
Return the plugin selector definition as a json object.
- property plugin_selector_history: QuerySet | None
Return the plugin selector history serializer.
- Returns:
The plugin selector history queryset.
- Return type:
Optional[QuerySet]
- property plugin_selector_serializer: PluginSelectorSerializer | None
Return the plugin selector serializer.
- Returns:
The plugin selector serializer.
- Return type:
Optional[PluginSelectorSerializer]
- property ready: bool[source]
Return whether the plugin is ready.
- Returns:
True if the plugin is ready, False otherwise.
- Return type:
- Raises:
SmarterPluginError – If the UserProfile is not set or if any of the plugin components are not of the expected type.
- refresh()[source]
Refresh the plugin.
- Returns:
True if the plugin is ready after refresh, False otherwise.
- Return type:
- reinitialize_plugin()[source]
Reset all plugin-related properties to
None.This method is used to clear the internal state of the plugin instance, including all cached references to Django ORM models, serializers, and other plugin-specific objects. It is typically called during initialization or when reloading a plugin to ensure that no stale data remains.
The following attributes are reset:
_plugin_meta: The plugin metadata model instance._plugin_selector: The plugin selector model instance._plugin_prompt: The plugin prompt model instance._plugin_selector_history: The queryset for selector history._plugin_data: The plugin data model instance._plugin_prompt_serializer: The serializer for the plugin prompt._plugin_selector_serializer: The serializer for the plugin selector._plugin_meta_serializer: The serializer for the plugin metadata.
This ensures that subsequent operations on the plugin will re-fetch or re-create these objects as needed, avoiding issues with outdated or invalid references.
- rfc1034_compliant_str(val)
Generates a RFC 1034-compliant name string suitable for use as a DNS label or resource identifier.
- Parameters:
val (str) – The input string to convert to RFC 1034-compliant format.
- Returns:
A string that is: - lower case - contains only alphanumeric characters and hyphens - starts and ends with an alphanumeric character - has a maximum length of 63 characters
- Return type:
- Raises:
SmarterValueError – If the input is not a string or is empty after conversion.
Note
Underscores in the input are replaced with hyphens.
Invalid characters (anything other than a-z, 0-9, or ‘-’) are removed.
Leading and trailing hyphens are stripped.
The result is truncated to 63 characters if necessary.
Warning
This function is intended for generating DNS-safe names. It does not guarantee uniqueness or suitability for all RFC 1034 use cases.
Example usage:
from smarter.common.utils import rfc1034_compliant_str # Basic usage print(rfc1034_compliant_str("My_ChatBot_2025")) # Output: my-chatbot-2025 # With special characters print(rfc1034_compliant_str("My@Bot!_Name")) # Output: my-bot-name # With long input long_name = "ThisIsAReallyLongChatBotNameThatShouldBeTruncatedToSixtyThreeCharacters_Extra" print(rfc1034_compliant_str(long_name)) # Output: thisisareallylongchatbotnamethatshouldbetruncatedtosixtythreecharacters
- rfc1034_compliant_to_snake(val)
Converts a RFC 1034-compliant name (typically used for DNS labels or resource identifiers) to a more human-readable
snake_casename.This function is useful for translating machine-friendly names (which use hyphens as word separators) into Pythonic identifiers (which use underscores).
- Parameters:
val (str) – The RFC 1034-compliant name to convert. This should be a string containing only lowercase letters, numbers, and hyphens.
- Returns:
The converted name in
snake_caseformat, with hyphens replaced by underscores.- Return type:
- Raises:
SmarterValueError – If the input is not a string.
Note
Only hyphens are replaced; other characters are preserved.
The function does not validate that the input is strictly RFC 1034-compliant. It assumes the input is already sanitized.
Warning
This function does not handle conversion of other non-alphanumeric characters. If the input contains characters other than hyphens, underscores, letters, or numbers, they will remain unchanged.
Example usage:
from smarter.common.utils import rfc1034_compliant_to_snake # Basic conversion print(rfc1034_compliant_to_snake("my-chatbot-2025")) # Output: my_chatbot_2025 # Input with no hyphens print(rfc1034_compliant_to_snake("simplelabel")) # Output: simplelabel # Input with multiple hyphens print(rfc1034_compliant_to_snake("this-is-a-test-label")) # Output: this_is_a_test_label # Input with invalid type try: rfc1034_compliant_to_snake(12345) except SmarterValueError as e: print(e) # Output: Could not convert RFC 1034 compliant name from <class 'int'>
- save()[source]
Save a plugin.
- Returns:
True if the plugin was saved successfully, False otherwise.
- Return type:
- Raises:
SmarterPluginError – If the plugin meta, selector, prompt, or data is not set or is not the correct instance.
- selected(user, input_text=None, messages=None)[source]
Determine whether the plugin should be selected for a given user and input context.
This method evaluates plugin selection logic based on the plugin’s selector directive, search terms, and optionally provided input text or message history. It supports both direct text matching and message-based matching, using the plugin’s configured search terms.
Selection is performed as follows:
If the plugin selector directive is set to
ALWAYS, the plugin is automatically selected.If
input_textis provided, the method checks if any search term matches the input using the NLP utilitydoes_refer_to. If a match is found, the plugin is selected and a signal is sent.If
messagesare provided, the method iterates through user messages and checks for matches against the search terms. If a match is found, the plugin is selected and a signal is sent.
Signals:
plugin_selectedis sent when the plugin is selected via either input text or messages.
- Parameters:
- Returns:
True if the plugin is selected, False otherwise.
- Return type:
Note
This method requires the plugin to be ready. If the plugin is not ready, it will return False. The method also updates the internal
_selectedstate when a match is found.
- smarter_build_absolute_uri(request)
Attempts to get the absolute URI from a request object.
This utility function tries to retrieve the request URL from any valid child class of
django.http.HttpRequest. It is especially useful in unit tests or scenarios where the request object may not implementbuild_absolute_uri().- Parameters:
request (Optional[HttpRequest]) – The request object.
- Returns:
The absolute request URL.
- Return type:
Optional[str]
- Raises:
SmarterValueError – If the URI cannot be built from the request.
- snake_to_camel(data, convert_values=False)
Converts snake_case strings, dictionary keys, or lists of such, to camelCase format.
- Parameters:
- Returns:
The converted data in camelCase format. Returns a string, dictionary, or list, matching the input type.
- Return type:
Note
For dictionaries, only keys are converted by default. If
convert_valuesis set, string values are also converted.Nested dictionaries and lists are processed recursively.
Warning
If the input is not a string, dictionary, or list, a
SmarterValueErroris raised.Example usage:
from smarter.common.utils import snake_to_camel # Convert a string print(snake_to_camel("user_name")) # Output: userName # Convert a dictionary data = { "user_name": "alice", "user_profile": { "first_name": "Alice", "last_name": "Smith" } } print(snake_to_camel(data)) # Output: {'userName': 'alice', 'userProfile': {'firstName': 'Alice', 'lastName': 'Smith'}} # Convert a list of strings print(snake_to_camel(["first_name", "last_name"])) # Output: ['firstName', 'lastName'] # Convert values as well data = {"user_name": "first_name"} print(snake_to_camel(data, convert_values=True)) # Output: {'userName': 'firstName'}
- sorted_dict(data)
Returns a new dictionary with keys sorted.
- to_json(version='v1')[source]
Serialize the plugin as a JSON-compatible dictionary suitable for Pydantic import.
This method generates a manifest representation of the plugin, combining Django ORM model data and serializer output into a structured dictionary that matches the Smarter API schema. It is primarily used to render plugin manifests for API responses and for interoperability with Pydantic models.
The returned dictionary includes the following top-level keys:
apiVersion: The manifest API version.kind: The manifest kind (plugin type).metadata: Plugin metadata, serialized from the Django ORM.spec: Specification section containing selector, prompt, and data.status: Status information including IDs, account number, username, and timestamps.
The
specsection is composed of:selector: Serialized plugin selector.prompt: Serialized plugin prompt.data: Serialized plugin data.
The
statussection includes:id: PluginMeta primary key.accountNumber: Associated account number.username: Username of the plugin owner.created: ISO-formatted creation timestamp.updated: ISO-formatted update timestamp.
- Parameters:
version (str) – The manifest version to serialize. Only
"v1"is supported.- Returns:
A dictionary representing the plugin manifest, or None if not ready.
- Return type:
- Raises:
SmarterConfigurationError – If serialization fails or the output is not a dictionary.
SmarterPluginError – If an unsupported version is requested.
Note
This method requires the plugin to be fully initialized and ready. All serializer properties must return valid data. If the plugin is not ready, None is returned.
- abstractmethod tool_call_fetch_plugin_response(function_args)[source]
Fetch information from a Plugin object.
- property unformatted_class_name: str
Returns the raw class name without formatting.
- Returns:
The unformatted class name as a string.
- Return type:
This is useful for logging or serialization where the plain class name is needed.
- update()[source]
Update a plugin.
- Returns:
True if the plugin was updated successfully, False otherwise.
- Return type:
- Raises:
SmarterPluginError – If the plugin manifest is not set or the plugin does not exist.
- property user: AnonymousUser | User | None
Returns the user for the current user. Handle lazy instantiation from user_profile or account.
- Returns:
The user for the current user.
- Return type:
User or None
- property user_profile: UserProfile | None
Returns the user_profile for the current user. Handle lazy instantiation from user or account.
- Returns:
The user_profile for the current user.
- Return type:
UserProfile or None
- property yaml: str
Return the plugin as a yaml string.
- Returns:
The plugin as a yaml string.
- Return type:
- Raises:
SmarterPluginError – If the plugin is not ready.
- yaml_to_json(yaml_string)[source]
Convert a yaml string to a dictionary.
- Parameters:
yaml_string (str) – The yaml string to convert.
- Returns:
The converted dictionary.
- Return type:
- Raises:
SmarterPluginError – If the yaml string is not valid.
- exception smarter.apps.plugin.plugin.base.SmarterPluginError(message='')[source]
Bases:
SmarterExceptionBase exception for Smarter API Plugin handling.
- __init__(message='')
- add_note(object, /)
Exception.add_note(note) – add a note to the exception
- args
- property get_formatted_err_message
- with_traceback(object, /)
Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.