API Contracts

smarter.models.chat

Models of the Smarter Chat api contract, version 2: an LLMClient’s configuration, its prompt api’s request and response, and the prompt’s progress events.

These mirror smarter.apps.prompt.contract.models in the Smarter platform, whose JSON Schema is chat-contract.schema.json. The SDK always asks for version 2 of the contract (with the X-Smarter-Capabilities: chat-contract-v2 request header), so the deprecated version 1 models are not mirrored.

Two deliberate differences from the platform’s models, both so that an SDK keeps working when the platform is upgraded:

  • every model allows extra fields, including the models of Django rows, whose platform models forbid them, and

  • values that the platform may add to, such as a plugin’s class or a progress event’s type, are str rather than Literal. Their known values are listed in the field descriptions.

The SDK’s tests validate the platform’s published example of every contract model against these models. See scripts/sync-contracts.sh.

smarter.models.chat.CHAT_CONTRACT_V2 = 2

The version of the Smarter Chat api contract that the SDK reads.

smarter.models.chat.CHAT_CONTRACT_V2_CAPABILITY = 'chat-contract-v2'

The X-Smarter-Capabilities value with which the SDK asks for version 2 of the contract.

smarter.models.chat.LLM_MESSAGE_ROLES = ('system', 'assistant', 'user', 'tool')

The message roles that the LLM providers accept.

Smarter’s own roles, smarter and smarter_error, are informational.

class smarter.models.chat.PromptChatApiAudioPart(*, type: Literal['input_audio'], input_audio: PromptChatApiInputAudio, **extra_data: Any)[source]

Bases: SmarterModel

An audio part of a message’s content.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiChoice(*, index: int = 0, finish_reason: str | None = None, message: PromptChatApiMessage | None = None, **extra_data: Any)[source]

Bases: SmarterModel

A choice of a chat completion.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiCompletion(*, id: str | None = None, object: str | None = None, created: int | None = None, model: str | None = None, choices: list[PromptChatApiChoice] = <factory>, usage: PromptChatApiUsage | None = None, system_fingerprint: str | None = None, service_tier: str | None = None, **extra_data: Any)[source]

Bases: SmarterModel

The LLM’s chat completion, in the format of the OpenAI api.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiConfig(*, contract: int = 2, session_key: str, sandbox_mode: bool, debug_mode: bool, meta_data: PromptChatApiMetaData, llmclient: PromptChatApiLLMClient, history: PromptChatApiHistory, plugins: PromptChatApiPluginList, extensions: PromptChatApiExtensionList | None = None, **extra_data: Any)[source]

Bases: SmarterModel

An LLMClient’s chat configuration: the LLMClient, the chat session’s key and history, and its plugins and extensions.

A new session’s configuration has an empty history. Pass its session_key to the prompt api to continue the session.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiConfigRequest(*, session_key: str | None = None, **extra_data: Any)[source]

Bases: SmarterModel

The configuration api’s request body.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiConfigResponse(*, api: str, metadata: PromptChatApiJournalMetadata = <factory>, data: PromptChatApiConfig, **extra_data: Any)[source]

Bases: PromptChatApiEnvelope

The configuration api’s response.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

smarter.models.chat.PromptChatApiContentPart

A part of a message whose content is a list of parts, by its type.

alias of Annotated[PromptChatApiTextPart | PromptChatApiImagePart | PromptChatApiAudioPart | PromptChatApiFilePart | PromptChatApiRefusalPart, FieldInfo(annotation=NoneType, required=True, discriminator=’type’)]

class smarter.models.chat.PromptChatApiEnvelope(*, api: str, metadata: PromptChatApiJournalMetadata = <factory>, **extra_data: Any)[source]

Bases: SmarterModel

The fields that every chat response adds beside data.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiErrorBody(*, errorClass: str, description: str, status: int, **extra_data: Any)[source]

Bases: SmarterModel

The error of a chat error response.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiErrorResponse(*, error: PromptChatApiErrorBody, api: str | None = None, metadata: PromptChatApiJournalMetadata | None = None, **extra_data: Any)[source]

Bases: SmarterModel

A chat error response.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiExtensionList(*, meta_data: PromptChatApiListMetaData, extensions: list[PromptChatApiLLMClientExtension] = <factory>, **extra_data: Any)[source]

Bases: SmarterModel

The LLMClient’s extensions.

The list itself is returned only in the complete configuration, with verbose=True. Otherwise extensions is empty, and meta_data has the counts.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiFileRef(*, file_data: str | None = None, file_id: str | None = None, filename: str | None = None, **extra_data: Any)[source]

Bases: SmarterModel

A file, base64 encoded, or by the provider’s file id.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiFilePart(*, type: Literal['file'], file: PromptChatApiFileRef, **extra_data: Any)[source]

Bases: SmarterModel

A file part of a message’s content.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiFunctionCall(*, name: str, arguments: str, **extra_data: Any)[source]

Bases: SmarterModel

The function that a tool call calls.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiHistory(*, chat_history: list[PromptChatApiMessage] = <factory>, prompt_tool_call_history: list[PromptChatApiPromptToolCall] | None = None, prompt_plugin_usage_history: list[PromptChatApiPromptPluginUsage] | None = None, plugin_selector_history: list[PromptChatApiPluginSelection] | None = None, llmclient_request_history: list[PromptChatApiLLMClientRequest] | None = None, **extra_data: Any)[source]

Bases: SmarterModel

The chat session’s history.

prompt_tool_call_history is returned only in the complete configuration, with verbose=True.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiImagePart(*, type: Literal['image_url'], image_url: PromptChatApiImageUrl, **extra_data: Any)[source]

Bases: SmarterModel

An image part of a message’s content.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiImageUrl(*, url: str, detail: Literal['auto', 'low', 'high'] | None = None, **extra_data: Any)[source]

Bases: SmarterModel

An image, by its url or as a data url.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiInputAudio(*, data: str, format: Literal['wav', 'mp3'], **extra_data: Any)[source]

Bases: SmarterModel

Audio, base64 encoded.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiJournalMetadata(*, command: str | None = None, thing: str | None = None, key: str | None = None, **extra_data: Any)[source]

Bases: SmarterModel

The metadata of a chat response.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiLLMClient(*, id: int, name: str, url_llmclient: str | None = None, version: str | None = None, deployed: bool | None = None, provider: str | None = None, default_model: str | None = None, default_system_role: str | None = None, app_name: str | None = None, app_assistant: str | None = None, app_welcome_message: str | None = None, app_example_prompts: list[str] | None = None, app_placeholder: str | None = None, app_info_url: str | None = None, app_file_attachment: bool | None = None, styles: PromptChatApiStyles | None = None, total_tools: int | None = None, **extra_data: Any)[source]

Bases: SmarterModel

The LLMClient, as its configuration describes it.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiLLMClientExtension(*, id: int, created_at: datetime | None = None, updated_at: datetime | None = None, llmclient: int, extension: int, name: str, version: str | None = None, enabled: bool, **extra_data: Any)[source]

Bases: PromptChatApiOrmModel

An extension of the LLMClient, whose tools it offers the LLM.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiLLMClientPlugin(*, id: int, created_at: datetime | None = None, updated_at: datetime | None = None, llmclient: int, plugin_meta: PromptChatApiPluginMeta, **extra_data: Any)[source]

Bases: PromptChatApiOrmModel

A plugin of the LLMClient.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiLLMClientRequest(*, id: int, created_at: datetime | None = None, updated_at: datetime | None = None, llmclient: int, request: PromptChatApiPromptRequest | None = None, session_key: str | None = None, is_aggregation: bool | None = None, **extra_data: Any)[source]

Bases: PromptChatApiOrmModel

A request that Smarter sent to the LLM.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiListMetaData(*, total_plugins: int | None = None, plugins_returned: int | None = None, total_extensions: int | None = None, extensions_returned: int | None = None, **extra_data: Any)[source]

Bases: SmarterModel

The counts of a list of the LLMClient’s plugins or extensions.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiMessage(*, role: str, content: str | list[Annotated[PromptChatApiTextPart | PromptChatApiImagePart | PromptChatApiAudioPart | PromptChatApiFilePart | PromptChatApiRefusalPart, FieldInfo(annotation=NoneType, required=True, discriminator='type')]] | None = None, tool_calls: list[PromptChatApiToolCall] | None = None, tool_call_id: str | None = None, name: str | None = None, **extra_data: Any)[source]

Bases: SmarterModel

A message of a chat thread, in the format of the OpenAI api.

Smarter adds its own roles: smarter for informational messages (e.g. the prompt’s configuration, the tools presented to the LLM, and its token charges), and smarter_error. These are saved in the session’s history, and are never sent to the LLM.

property text: str

The message’s text: its content, or the text of its text parts, joined by blank lines.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiMetaData(*, ready: bool | None = None, is_valid: bool | None = None, is_deployed: bool | None = None, is_authentication_required: bool | None = None, is_custom_domain: bool | None = None, **extra_data: Any)[source]

Bases: SmarterModel

The LLMClient’s state.

The complete configuration, from config() with verbose=True, adds the platform’s other details of the LLMClient, which this model accepts as extra fields.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiOrmModel(*, id: int, created_at: datetime | None = None, updated_at: datetime | None = None, **extra_data: Any)[source]

Bases: SmarterModel

The fields of a Django row that the contract returns.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiPluginList(*, meta_data: PromptChatApiListMetaData, plugins: list[PromptChatApiLLMClientPlugin] = <factory>, **extra_data: Any)[source]

Bases: SmarterModel

The LLMClient’s plugins.

The list itself is returned only in the complete configuration, with verbose=True. Otherwise plugins is empty, and meta_data has the counts.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiPluginMeta(*, id: int, created_at: datetime | None = None, updated_at: datetime | None = None, name: str, description: str | None = None, version: str | None = None, tags: list[str] = [], annotations: list[dict[str, JsonValue]] | dict[str, JsonValue] | None = None, plugin_class: str, **extra_data: Any)[source]

Bases: PromptChatApiOrmModel

A plugin’s metadata.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiPluginSelection(*, id: int, created_at: datetime | None = None, updated_at: datetime | None = None, plugin_selector: int, search_term: str | None = None, messages: list[PromptChatApiMessage] | None = None, session_key: str | None = None, **extra_data: Any)[source]

Bases: PromptChatApiOrmModel

A plugin selector’s decision in the chat session.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiProgressEvent(*, type: str, message: str, **extra_data: Any)[source]

Bases: SmarterModel

The data of a progress event: a step of a running prompt.

Events may carry more fields, depending on their type, e.g. tool and arguments.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiProgressResult(*, status: int, response: PromptChatApiPromptResponse | PromptChatApiErrorResponse, **extra_data: Any)[source]

Bases: SmarterModel

The data of the final result event.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiPromptPluginUsage(*, id: int, created_at: datetime | None = None, updated_at: datetime | None = None, prompt: int, plugin: int, input_text: str | None = None, **extra_data: Any)[source]

Bases: PromptChatApiOrmModel

A use of a plugin in the chat session.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiPromptRequest(*, session_key: str | None = None, messages: list[PromptChatApiMessage], **extra_data: Any)[source]

Bases: SmarterModel

The prompt api’s request body.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiPromptResponse(*, api: str, metadata: PromptChatApiJournalMetadata = <factory>, data: PromptChatApiPromptResult, error: PromptChatApiErrorBody | None = None, **extra_data: Any)[source]

Bases: PromptChatApiEnvelope

The prompt api’s response.

A failed prompt has error too, and its data.messages include a smarter_error message.

property text: str

The text of the LLM’s final reply.

See PromptChatApiPromptResult.text.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiPromptResult(*, contract: int = 2, status: int, messages: list[PromptChatApiMessage] = <factory>, completion: PromptChatApiCompletion | None = None, plugins: list[str] = <factory>, tools: list[str] = <factory>, **extra_data: Any)[source]

Bases: SmarterModel

The result of a prompt.

property assistant_messages: list[PromptChatApiMessage]

The assistant messages that the prompt added, which are the LLM’s replies and tool calls.

property text: str

The text of the LLM’s final reply: the last assistant message that has content, or an empty string.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiPromptToolCall(*, id: int, created_at: datetime | None = None, updated_at: datetime | None = None, prompt: int, plugin: int | None = None, function_name: str | None = None, function_args: str | None = None, request: JsonValue = None, response: JsonValue = None, **extra_data: Any)[source]

Bases: PromptChatApiOrmModel

A tool call of the chat session.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiRefusalPart(*, type: Literal['refusal'], refusal: str, **extra_data: Any)[source]

Bases: SmarterModel

The LLM’s refusal, as a part of a message’s content.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiStyleSection(*, background_color: str | None = None, color: str | None = None, border_color: str | None = None, info_color: str | None = None, link_color: str | None = None, editor_background_color: str | None = None, placeholder_color: str | None = None, **extra_data: Any)[source]

Bases: SmarterModel

The colors of one section of the chat ui.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiStyles(*, app_logo_url: str | None = None, app_background_image_url: str | None = None, app_background_image_size: str | None = None, assistant_avatar_url: str | None = None, user_avatar_url: str | None = None, font_family: str | None = None, google_font: str | None = None, font_size: str | None = None, border_radius: str | None = None, main_container: PromptChatApiStyleSection | None = None, conversation_header: PromptChatApiStyleSection | None = None, message_list: PromptChatApiStyleSection | None = None, incoming_message: PromptChatApiStyleSection | None = None, outgoing_message: PromptChatApiStyleSection | None = None, message_input: PromptChatApiStyleSection | None = None, buttons: PromptChatApiStyleSection | None = None, typing_indicator: PromptChatApiStyleSection | None = None, **extra_data: Any)[source]

Bases: SmarterModel

The LLMClient’s chat ui styles.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiTextPart(*, type: Literal['text'], text: str, **extra_data: Any)[source]

Bases: SmarterModel

A text part of a message’s content.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiTokenDetails(*, reasoning_tokens: int | None = None, cached_tokens: int | None = None, audio_tokens: int | None = None, **extra_data: Any)[source]

Bases: SmarterModel

The breakdown of a completion’s tokens.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiToolCall(*, id: str, type: str = 'function', function: PromptChatApiFunctionCall, **extra_data: Any)[source]

Bases: SmarterModel

A tool call of an assistant message.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.chat.PromptChatApiUsage(*, prompt_tokens: int | None = None, completion_tokens: int | None = None, total_tokens: int | None = None, prompt_tokens_details: PromptChatApiTokenDetails | None = None, completion_tokens_details: PromptChatApiTokenDetails | None = None, **extra_data: Any)[source]

Bases: SmarterModel

A chat completion’s token usage.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

smarter.models.journal

Models of the Smarter Journal api contract: the envelope of every journaled response, its error body, and a journal entry.

The Smarter platform journals its api responses. Each one carries api (the api version, e.g. smarter.sh/v1) and metadata (the command and the resource kind, which the platform calls its “thing”) beside its own data, message or error.

These models mirror smarter.lib.journal.contract.models in the Smarter platform, whose JSON Schema is journal-contract.schema.json. The SDK’s tests validate the platform’s schema against them. See scripts/sync-contracts.sh.

class smarter.models.journal.JournalEntry(*, key: str, createdAt: datetime | None = None, user: str | None = None, thing: str, command: str, statusCode: int, request: JsonValue = None, response: JsonValue = None, **extra_data: Any)[source]

Bases: SmarterCamelModel

A Smarter Journal entry, as /api/v1/journal/ returns it.

The list returns entries without request and response. Retrieving one entry by its key includes them. Only superusers may read the journal, because the request documents hold the callers’ credentials.

model_config = {'alias_generator': <function to_camel>, 'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.journal.JournalPage(*, objects: list[JournalEntry] = [], pagination: JournalPagination, **extra_data: Any)[source]

Bases: SmarterModel

One page of the journal’s entries.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.journal.JournalPagination(*, page: int, pageSize: int, numPages: int, count: int, search: str = '', ordering: str = '', sortFields: list[str] = [], filter: str = '', filters: list[str] = <factory>, **extra_data: Any)[source]

Bases: SmarterCamelModel

The description of one page of the journal’s entries.

model_config = {'alias_generator': <function to_camel>, 'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.journal.SAMJournalApiEnvelope(*, api: str = 'smarter.sh/v1', metadata: SAMJournalApiMetadata | None = None, **extra_data: Any)[source]

Bases: SmarterModel

The fields that every journaled response adds beside its own data.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.journal.SAMJournalApiErrorBody(*, errorClass: str, stacktrace: str | None = None, description: str | None = None, status: int, args: JsonValue = None, cause: str | None = None, context: str | None = None, thing: str | None = None, command: str | None = None, **extra_data: Any)[source]

Bases: SmarterModel

The error of a journaled error response.

Every error of the cli api has this shape.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.journal.SAMJournalApiErrorResponse(*, api: str = 'smarter.sh/v1', metadata: SAMJournalApiMetadata | None = None, error: SAMJournalApiErrorBody, **extra_data: Any)[source]

Bases: SAMJournalApiEnvelope

The body of a journaled error response.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.journal.SAMJournalApiMetadata(*, command: str, thing: str, key: str | None = None, **extra_data: Any)[source]

Bases: SmarterModel

The metadata of a journaled response: its command and thing, and the key of its journal entry.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.journal.SmarterJournalMessage(*, api: str = 'smarter.sh/v1', metadata: SAMJournalApiMetadata | None = None, message: str | None = None, **extra_data: Any)[source]

Bases: SAMJournalApiEnvelope

A journaled response that carries only a message, e.g. the response of delete or deploy.

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

class smarter.models.journal.SmarterJournaledResponse(*, api: str = 'smarter.sh/v1', metadata: SAMJournalApiMetadata | None = None, data: DataT | None = None, message: str | None = None, **extra_data: Any)[source]

Bases: SAMJournalApiEnvelope, Generic[DataT]

A successful journaled response: the envelope, the response’s data, and an optional message.

data is typed by the endpoint, e.g. SmarterJournaledResponse[WhoAmI].

model_config = {'extra': 'allow', 'populate_by_name': True, 'protected_namespaces': ()}

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

smarter.models.react

Pydantic models of the Smarter platform’s Django-React integration: a React app’s Vite manifest, and the assets that a template includes.

The platform’s web console pages are React apps that Django hosts. SmarterReactTemplateTagManager (in smarter.lib.django.templatetags) reads each app’s Vite-generated manifest.json, and each <app>_react_assets template tag returns the files that a template includes. These models are a copy of the platform’s smarter.lib.django.templatetags.smarter_react_contract, which is pure Pydantic, so that tools that build or check React apps for a Smarter platform can use them without the platform.

  • ViteManifest is a Vite-generated React manifest.json: a map of ViteManifestChunk keyed by source path. The manager validates the file against it when it loads it, and collects an entry point’s assets with its methods.

  • ReactAppAssets is what every <app>_react_assets template tag returns: the ordered JS and CSS files that a Django template includes for a React app.

The manifest is a third-party shape. ViteManifestChunk declares the fields that Vite documents, and allows extra fields, which newer Vite (or Rolldown) versions add, so that a Vite upgrade doesn’t break page rendering. Smarter’s own addition, the _custom entry that every package’s vite.config.ts writes after the build, is a ReactAppBuildInfo.

smarter.models.react.BUILD_INFO_KEY = '_custom'

The key of the build information that each React package’s vite.config.ts adds to its manifest.json.

class smarter.models.react.ReactAppAssets(*, js: list[str] = <factory>, css: list[str] = <factory>)[source]

Bases: BaseModel

The ordered JS and CSS files of a React app’s entry point, which a Django template includes.

Every <app>_react_assets template tag returns one. Templates read its attributes, e.g. {% for css_file in assets.css %}.

Example:

{
    "js": [
        "assets/index-CZK_Bxxh.js",
        "assets/rolldown-runtime-B3igc2qu.js",
        "assets/xterm-D5XSfLrr.js"
    ],
    "css": [
        "assets/index-58MXwt-L.css",
        "assets/xterm-kHJ-D0s7.css"
    ]
}
model_config = {'extra': 'forbid'}

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

class smarter.models.react.ReactAppBuildInfo(*, buildTime: datetime, version: str, buildEnv: str)[source]

Bases: BaseModel

The build information that each React package’s vite.config.ts adds to its manifest.json, as _custom.

Example:

"_custom": {
    "buildTime": "2026-10-12T01:09:47.988Z",
    "version": "0.3.0",
    "buildEnv": "production"
}
model_config = {'extra': 'forbid', 'populate_by_name': True}

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

smarter.models.react.ReactAssetType

The kinds of asset that a React app’s entry point loads.

alias of Literal[‘js’, ‘css’]

class smarter.models.react.ViteManifest(*, chunks: dict[str, ViteManifestChunk], build_info: ReactAppBuildInfo | None = None)[source]

Bases: BaseModel

A React app’s Vite-generated manifest.json: its chunks, keyed by source path, and Smarter’s build information.

In the file, the build information is the _custom entry, beside the chunks. Validating a manifest.json splits it out into build_info, and dumping the model puts it back, so model_dump(by_alias=True) has the file’s shape.

See https://vite.dev/guide/backend-integration.

model_config = {'extra': 'forbid'}

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

classmethod split_build_info(data: object) → object[source]

Split a manifest.json’s _custom entry from its chunks.

Input that already has chunks, e.g. keyword arguments, is left as is.

Parameters:

data – The manifest.json’s content.

Returns:

The model’s fields.

Return type:

object

join_build_info(handler: SerializerFunctionWrapHandler) → dict[str, Any][source]

Dump the manifest in the shape of manifest.json, with the build information as its _custom entry.

Parameters:

handler – Pydantic’s default serializer.

Returns:

The chunks, keyed by source path, and _custom.

Return type:

SmarterDictType

entry_key() → str | None[source]

Find the key of the manifest’s entry point, the first chunk with "isEntry": true.

Returns:

The entry point’s key, or None if the manifest has no entry point.

Return type:

Optional[str]

collect_assets(key: str, asset_type: Literal['js', 'css'], seen: set[str] | None = None) → list[str][source]

Recursively collect the JS or CSS files of a chunk and of its static imports.

A chunk’s own files come first, followed by those of each of its imports, depth first, in the order they are listed. Each chunk is visited once, so circular imports terminate.

Parameters:
  • key – The manifest key of the chunk to collect assets for.

  • asset_type – "js" for the chunks’ file, "css" for their css files.

  • seen – The keys already visited, used by the recursion.

Returns:

The asset file paths, relative to the app’s static directory.

Return type:

list[str]

class smarter.models.react.ViteManifestChunk(*, file: str, name: str | None = None, names: list[str] = <factory>, src: str | None = None, isEntry: bool = False, isDynamicEntry: bool = False, imports: list[str] = <factory>, dynamicImports: list[str] = <factory>, css: list[str] = <factory>, assets: list[str] = <factory>, **extra_data: Any)[source]

Bases: BaseModel

One entry of a Vite-generated manifest.json: a chunk, or a static asset.

See https://vite.dev/guide/backend-integration. Extra fields are allowed, because Vite and Rolldown add their own.

Example:

"index.html": {
    "file": "assets/index.js",
    "name": "index",
    "src": "index.html",
    "isEntry": true,
    "imports": ["_rolldown-runtime.js", "_xterm.js"],
    "css": ["assets/index-DvLY75bJ.css"]
}
model_config = {'extra': 'allow', 'populate_by_name': True}

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