Views

The page that a browser sees at a deployed LLMClient’s endpoints.

A deployed LLMClient’s endpoints, /config/ and /prompt/, are api endpoints that answer only POST. A browser that opens one of them, or the LLMClient’s default url, instead receives a page about the LLMClient and The Smarter Project: what its url is and how it works, the curl commands that call the endpoints, the ways to put its chat on a web site, the LLMClient’s manifest, without the personal information in it, and how to build one. The page is served only while the enable_llmclient_endpoints_page waffle switch is active.

The Prompt Engineer’s Workbench shows the same content, whatever the switch, in the “Deployment” tab of its Console: see smarter.apps.prompt.views.detailviews.prompt_deployment_view.LLMClientDeploymentView. Both render the @smarter/llmclient-deployment React package’s page, this one as its app, the workbench as its component, and both build its data with deployment_page(), whose contract is smarter.apps.llmclient.views.deployment_contract.

See also

smarter.apps.llmclient.views.endpoints.ALLOWED_ORIGINS_YAML = 'spec:\n  config:\n    prompt:\n      allowedOrigins:\n        - https://www.example.com'

The part of an LLMClient’s manifest that allows a web site’s origin to call it.

smarter.apps.llmclient.views.endpoints.API_KEY_YAML = 'spec:\n  apiKey: <API-KEY-NAME>'

The part of an LLMClient’s manifest that attaches an api key to it, by the key’s name.

smarter.apps.llmclient.views.endpoints.BROWSER_METHODS = ('GET', 'HEAD')

The methods that browser_friendly() sends to LLMClientEndpointsView.

('GET', 'HEAD')
smarter.apps.llmclient.views.endpoints.DRUPAL_INSTALL_COMMANDS = 'composer config repositories.smarter-drupal vcs https://github.com/smarter-sh/smarter-drupal\ncomposer require smarter-sh/smarter-drupal\ndrush pm:install smarter_chat'

The commands that install Smarter Chat for Drupal.

smarter.apps.llmclient.views.endpoints.EMBED_SCRIPT_URL = 'https://cdn.jsdelivr.net/npm/@smarter.sh/ui-chat@0/dist/embed.js'

Smarter Chat’s embed script, on jsDelivr, which the page’s html snippet loads.

smarter.apps.llmclient.views.endpoints.EXAMPLE_PROMPT = 'hello world'

The prompt that the page’s example /prompt/ command sends, for an LLMClient without example prompts.

smarter.apps.llmclient.views.endpoints.EXAMPLE_RESPONSE_CONTENT = "## The LLM's answer\n\nIn **Markdown**: headings, lists, `code`, tables and [links](https://smarter.sh)."

The assistant message’s content in the page’s example /prompt/ response.

class smarter.apps.llmclient.views.endpoints.LLMClientEndpointsView(**kwargs)[source]

Bases: SmarterWebHtmlView

A deployed LLMClient’s endpoints, for a browser, and The Smarter Project, which built it.

The page is the @smarter/llmclient-deployment React app, in its public mode, whose data, deployment_page()’s public copy, is in the page, in a json_script. It names no owner, and has no api keys. A host that serves no deployed LLMClient is a 404, as it is for the endpoints themselves.

property formatted_class_name: str

Returns the class name in a formatted string along with the name of this view.

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

The page, for the LLMClient that serves the request’s host.

Parameters:

request (HttpRequest) – The request.

Returns:

The page, or a 404 if no deployed LLMClient serves the host.

Return type:

HttpResponse

template_path: str = 'llmclient/endpoints.html'
smarter.apps.llmclient.views.endpoints.LOGO_PATH = 'images/logo/smarter-crop.png'

The Smarter Project’s logo, on its cdn.

smarter.apps.llmclient.views.endpoints.MANIFEST_CACHE_TIMEOUT = 600

How long, in seconds, cached_manifest_yaml() keeps a manifest.

smarter.apps.llmclient.views.endpoints.MANIFEST_PII_EXCLUDE = {'status': {'username'}}

the personal information in it.

status.username is the owner’s username.

{'status': set(['username'])}
Type:

The fields that manifest_yaml() leaves out of the page’s manifest

smarter.apps.llmclient.views.endpoints.NPM_INSTALL_COMMAND = 'npm install @smarter.sh/ui-chat'

The command that installs Smarter Chat’s npm package.

smarter.apps.llmclient.views.endpoints.SESSION_KEY_PLACEHOLDER = '<SESSION-KEY>'

The placeholder of a chat session’s key in the page’s commands, which the config api’s response replaces.

smarter.apps.llmclient.views.endpoints.SWITCH_COMMAND = 'python manage.py waffle_switch enable_llmclient_endpoints_page off'

The command that turns off the page at deployed LLMClients’ urls, which the page shows administrators.

smarter.apps.llmclient.views.endpoints.api_keys_url()[source]

The url of the web console’s API Keys page, which lists the account’s api keys, and turns them on and off.

It is absolute, on the platform’s domain, because the page at a deployed LLMClient’s url is on the LLMClient’s own host, whose urlconf has none of the web console’s names.

Returns:

The url.

Return type:

str

smarter.apps.llmclient.views.endpoints.browser_friendly(api_view)[source]

Wrap a deployed LLMClient’s api view, so that a GET or HEAD receives LLMClientEndpointsView.

Only while the enable_llmclient_endpoints_page switch is active. Every other request, including a GET while it is inactive, goes to the api view. The wrapper keeps the api view’s attributes, e.g. csrf_exempt.

path("prompt/", browser_friendly(DefaultLLMClientApiView.as_view())),
Parameters:

api_view (Callable[..., HttpResponseBase]) – The api view, from as_view().

Returns:

The wrapped view.

Return type:

Callable[..., HttpResponseBase]

smarter.apps.llmclient.views.endpoints.cached_manifest_yaml(llmclient_id, updated_at)[source]

The YAML of an LLMClient’s manifest, for the page, which its broker takes about a second to build.

An update of the LLMClient changes updated_at, and so the key. A change to a resource that it refers to, e.g. a plugin added to it, shows after MANIFEST_CACHE_TIMEOUT.

Parameters:
  • llmclient_id (int) – The LLMClient’s id.

  • updated_at (str) – The LLMClient’s updated_at, in ISO 8601.

Returns:

The YAML.

Return type:

str

Raises:

SAMLLMClientBrokerError – If the broker can’t build the manifest, which is then not cached.

smarter.apps.llmclient.views.endpoints.create_api_key_command(account_number, llmclient_name)[source]

The command that a platform administrator runs to create an api key, which prints its secret once.

Parameters:
  • account_number (str) – The LLMClient’s account number.

  • llmclient_name (str) – The LLMClient’s name, for the key’s description.

Returns:

The command, one option per line.

Return type:

str

smarter.apps.llmclient.views.endpoints.curl_command(url, body='', authenticated=False)[source]

A curl command that POSTs to a deployed LLMClient’s endpoint, one option per line.

Parameters:
  • url (str) – The endpoint’s url.

  • body (str) – The JSON request body, or empty for none. It is quoted for a POSIX shell.

  • authenticated (bool) – True to send an api key, which an LLMClient with api keys requires.

Returns:

The command.

Return type:

str

smarter.apps.llmclient.views.endpoints.deployment_page(llmclient)[source]

The data of the LLMClient’s deployment page: the LLMClient, its url, and the snippets that call it.

An LLMClient that isn’t deployed has the url that it will have once it is, and deployed is false. The page is the workbench’s, without its owner or api keys, which LLMClientDeploymentView adds. The page at the LLMClient’s url is its public() copy.

Parameters:

llmclient (LLMClient) – The LLMClient.

Returns:

The page.

Return type:

LLMClientDeploymentPage

smarter.apps.llmclient.views.endpoints.embed_html(url, authenticated=False)[source]

The html that adds the LLMClient’s chat to any web page: Smarter Chat’s embed script, as a launcher.

Parameters:
  • url (str) – The LLMClient’s url.

  • authenticated (bool) – True to pass an api key, which an LLMClient with api keys requires.

Returns:

The html.

Return type:

str

smarter.apps.llmclient.views.endpoints.example_prompt(llmclient)[source]

The prompt that the page’s example /prompt/ command sends: the LLMClient’s first example prompt.

Parameters:

llmclient (LLMClient) – The LLMClient.

Returns:

Its first non-empty example prompt, or EXAMPLE_PROMPT if it has none.

Return type:

str

smarter.apps.llmclient.views.endpoints.example_response()[source]

An example of the /prompt/ endpoint’s response, abbreviated to the assistant’s Markdown message.

It is built from the chat api contract’s model, so that it can’t drift from the real response.

Returns:

The response, as indented JSON.

Return type:

str

smarter.apps.llmclient.views.endpoints.llmclient_manifest(llmclient)[source]

The LLMClient’s manifest, as smarter describe llmclient returns it, from its broker.

Parameters:

llmclient (LLMClient) – The LLMClient.

Returns:

The manifest.

Return type:

SAMLLMClient

Raises:

SAMLLMClientBrokerError – If the broker can’t build it.

smarter.apps.llmclient.views.endpoints.manifest_yaml(manifest)[source]

The manifest as YAML, in the order of its model’s fields, without MANIFEST_PII_EXCLUDE.

Parameters:

manifest (SAMLLMClient) – The manifest.

Returns:

The YAML.

Return type:

str

smarter.apps.llmclient.views.endpoints.react_snippet(url, authenticated=False)[source]

A React component that displays the LLMClient’s chat, with Smarter Chat’s npm package.

Parameters:
  • url (str) – The LLMClient’s url.

  • authenticated (bool) – True to pass an api key, which an LLMClient with api keys requires.

Returns:

The component’s TypeScript.

Return type:

str

The contract of the deployment page, which the @smarter/llmclient-deployment React app renders, at a deployed LLMClient’s url and in the workbench’s Deployment tab:

The contract of an LLMClient’s deployment page: the JSON that its React app renders, as Pydantic models.

The @smarter/llmclient-deployment React package renders the page in two places:

  • public: the page at a deployed LLMClient’s url, which anyone can open, LLMClientEndpointsView, which puts the page’s data in the page with Django’s json_script, so that the app renders without a request of its own;

  • workbench: the “Deployment” tab of the Prompt Engineer’s Workbench’s Console, which renders the package’s component, and fetches the page’s data from LLMClientDeploymentView when the tab is first opened. It adds the LLMClient’s owner and its api keys, and manages them in place.

Both views build a LLMClientDeploymentPage. The workbench’s api key actions are POSTs that answer with an ApiKeyActionResponse, the page as it now is, or an ErrorResponse. The JSON is camelCase.

manage.py export_llmclient_deployment_schema writes the models’ JSON Schema to llmclient-deployment.schema.json, beside this module, from which @smarter/llmclient-deployment generates its TypeScript types (make llmclient-deployment-contract). A test fails if the committed schema is out of date.

The public page names no owner and has no api keys: LLMClientDeploymentPage.public() leaves them out.

class smarter.apps.llmclient.views.deployment_contract.ApiKeyActionResponse(**data)[source]

Bases: LLMClientDeploymentModel

The page after an api key action, and a new key’s secret.

model_config = {'alias_generator': <function to_camel>, 'extra': 'forbid', 'json_schema_serialization_defaults_required': True, 'serialize_by_alias': True, 'validate_by_alias': True, 'validate_by_name': True}

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

new_api_key: NewApiKey | None
page: LLMClientDeploymentPage
class smarter.apps.llmclient.views.deployment_contract.AttachApiKeyRequest(**data)[source]

Bases: LLMClientDeploymentModel

Attach one of the account’s api keys to the LLMClient, in place of any other.

key_id: UUID
model_config = {'alias_generator': <function to_camel>, 'extra': 'forbid', 'json_schema_serialization_defaults_required': True, 'serialize_by_alias': True, 'validate_by_alias': True, 'validate_by_name': True}

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

class smarter.apps.llmclient.views.deployment_contract.CreateApiKeyRequest(**data)[source]

Bases: LLMClientDeploymentModel

Create an api key for the user, and attach it to the LLMClient if asked to.

attach: bool
description: str
model_config = {'alias_generator': <function to_camel>, 'extra': 'forbid', 'json_schema_serialization_defaults_required': True, 'serialize_by_alias': 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
class smarter.apps.llmclient.views.deployment_contract.DeploymentApiKey(**data)[source]

Bases: LLMClientDeploymentModel

One of the account’s api keys that the user may manage.

description: str
is_active: bool
is_attached: bool
key_id: UUID
last_used_at: datetime | None
model_config = {'alias_generator': <function to_camel>, 'extra': 'forbid', 'json_schema_serialization_defaults_required': True, 'serialize_by_alias': 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
owner: str
class smarter.apps.llmclient.views.deployment_contract.DeploymentApiKeys(**data)[source]

Bases: LLMClientDeploymentModel

The LLMClient’s api key, the account’s api keys, what the user may do, and where.

Workbench only.

api_keys: list[DeploymentApiKey]
attach_url: str
attached: DeploymentAttachedApiKey | None
can_create: bool
can_manage: bool
create_url: str
detach_url: str
model_config = {'alias_generator': <function to_camel>, 'extra': 'forbid', 'json_schema_serialization_defaults_required': True, 'serialize_by_alias': True, 'validate_by_alias': True, 'validate_by_name': True}

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

class smarter.apps.llmclient.views.deployment_contract.DeploymentAttachedApiKey(**data)[source]

Bases: LLMClientDeploymentModel

The LLMClient’s api key, which every request to its url needs, while it is active.

is_active: bool
model_config = {'alias_generator': <function to_camel>, 'extra': 'forbid', 'json_schema_serialization_defaults_required': True, 'serialize_by_alias': 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
class smarter.apps.llmclient.views.deployment_contract.DeploymentLLMClient(**data)[source]

Bases: LLMClientDeploymentModel

The LLMClient, as the page describes it.

app_name: str
example_prompts: list[str]
model: str | None
model_config = {'alias_generator': <function to_camel>, 'extra': 'forbid', 'json_schema_serialization_defaults_required': True, 'serialize_by_alias': 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
provider: str | None
url: str
version: str | None
workbench_url: str

Bases: LLMClientDeploymentModel

The urls that the page links to, which depend on the platform’s configuration.

api_keys_url: str
docs_url: str
logo_url: str
marketing_site_url: str
model_config = {'alias_generator': <function to_camel>, 'extra': 'forbid', 'json_schema_serialization_defaults_required': True, 'serialize_by_alias': True, 'validate_by_alias': True, 'validate_by_name': True}

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

product_description: str
product_name: str
class smarter.apps.llmclient.views.deployment_contract.DeploymentOwnership(**data)[source]

Bases: LLMClientDeploymentModel

Who owns the LLMClient, and how a user who may not change it can clone it.

Workbench only.

can_edit: bool
llmclient_list_url: str
model_config = {'alias_generator': <function to_camel>, 'extra': 'forbid', 'json_schema_serialization_defaults_required': True, 'serialize_by_alias': True, 'validate_by_alias': True, 'validate_by_name': True}

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

owner: str
ownership_docs_url: str
class smarter.apps.llmclient.views.deployment_contract.DeploymentSnippets(**data)[source]

Bases: LLMClientDeploymentModel

The commands, manifest excerpts and code that the page shows, each in a terminal with a Copy button.

allowed_origins_yaml: str
api_key_yaml: str
config_command: str
create_api_key_command: str
deploy_command: str
deploy_commands: str
drupal_install_commands: str
embed_html: str
embed_html_with_api_key: str
example_response: str
model_config = {'alias_generator': <function to_camel>, 'extra': 'forbid', 'json_schema_serialization_defaults_required': True, 'serialize_by_alias': True, 'validate_by_alias': True, 'validate_by_name': True}

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

npm_install_command: str
prompt_command: str
react_snippet: str
react_snippet_with_api_key: str
session_config_command: str
session_prompt_command: str
switch_command: str
smarter.apps.llmclient.views.deployment_contract.ErrorCode

Why an api key action failed, which the React app explains in the user’s language.

alias of Literal[‘not_found’, ‘invalid_request’, ‘not_owner’, ‘not_admin’, ‘not_account_admin’, ‘invalid_name’, ‘duplicate_name’, ‘api_key_not_found’]

class smarter.apps.llmclient.views.deployment_contract.ErrorResponse(**data)[source]

Bases: LLMClientDeploymentModel

Why an api key action failed: its code, and an English message for logs and developers.

code: Literal['not_found', 'invalid_request', 'not_owner', 'not_admin', 'not_account_admin', 'invalid_name', 'duplicate_name', 'api_key_not_found']
error: str
model_config = {'alias_generator': <function to_camel>, 'extra': 'forbid', 'json_schema_serialization_defaults_required': True, 'serialize_by_alias': True, 'validate_by_alias': True, 'validate_by_name': True}

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

class smarter.apps.llmclient.views.deployment_contract.LLMClientDeploymentModel(**data)[source]

Bases: BaseModel

The base class of the contract’s models: camelCase JSON, and no fields that a model doesn’t define.

model_config = {'alias_generator': <function to_camel>, 'extra': 'forbid', 'json_schema_serialization_defaults_required': True, 'serialize_by_alias': True, 'validate_by_alias': True, 'validate_by_name': True}

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

to_json()[source]

The model as the page and the api send it.

Return type:

dict[str, Any]

class smarter.apps.llmclient.views.deployment_contract.LLMClientDeploymentPage(**data)[source]

Bases: LLMClientDeploymentModel

Everything that the page shows.

api_keys: DeploymentApiKeys | None
authenticated: bool
deployed: bool
llmclient: DeploymentLLMClient
manifest_yaml: str | None
mode: Literal['public', 'workbench']
model_config = {'alias_generator': <function to_camel>, 'extra': 'forbid', 'json_schema_serialization_defaults_required': True, 'serialize_by_alias': True, 'validate_by_alias': True, 'validate_by_name': True}

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

ownership: DeploymentOwnership | None
public()[source]

The page at the deployed LLMClient’s url, which anyone can open: without its owner or api keys.

Returns:

A copy of the page.

Return type:

LLMClientDeploymentPage

snippets: DeploymentSnippets
smarter.apps.llmclient.views.deployment_contract.PageMode

at the deployed LLMClient’s url, or in the workbench’s “Deployment” tab.

Type:

Where the page is

alias of Literal[‘public’, ‘workbench’]

smarter.apps.llmclient.views.deployment_contract.REQUEST_MODELS: tuple[type[BaseModel], ...] = (<class 'smarter.apps.llmclient.views.deployment_contract.CreateApiKeyRequest'>, <class 'smarter.apps.llmclient.views.deployment_contract.AttachApiKeyRequest'>)

fields with defaults may be left out.

(
    <class 'smarter.apps.llmclient.views.deployment_contract.CreateApiKeyRequest'>,
    <class 'smarter.apps.llmclient.views.deployment_contract.AttachApiKeyRequest'>,
)
Type:

What the api accepts

smarter.apps.llmclient.views.deployment_contract.RESPONSE_MODELS: tuple[type[BaseModel], ...] = (<class 'smarter.apps.llmclient.views.deployment_contract.LLMClientDeploymentPage'>, <class 'smarter.apps.llmclient.views.deployment_contract.ApiKeyActionResponse'>, <class 'smarter.apps.llmclient.views.deployment_contract.ErrorResponse'>)

every field, always.

(
    <class 'smarter.apps.llmclient.views.deployment_contract.LLMClientDeploymentPage'>,
    <class 'smarter.apps.llmclient.views.deployment_contract.ApiKeyActionResponse'>,
    <class 'smarter.apps.llmclient.views.deployment_contract.ErrorResponse'>,
)
Type:

What the views send

smarter.apps.llmclient.views.deployment_contract.SCHEMA_PATH = PosixPath('/Users/mcdaniel/Desktop/gh/smarter-sh/smarter/smarter/smarter/apps/llmclient/views/llmclient-deployment.schema.json')

The committed JSON Schema of the contract.

smarter.apps.llmclient.views.deployment_contract.contract_schema()[source]

The contract’s JSON Schema (draft 2020-12): one definition, in $defs, per model.

Return type:

dict[str, Any]

smarter.apps.llmclient.views.deployment_contract.contract_schema_json()[source]

The contract’s JSON Schema, as the text of llmclient-deployment.schema.json.

Return type:

str

Django template tags for the LLMClient deployment React app, @smarter/llmclient-deployment.

The app renders the page at a deployed LLMClient’s url, llmclient/endpoints.html, whose root element and json_script these ids name. The “Deployment” tab of the Prompt Engineer’s Workbench renders the same page, with the package’s component, in the @smarter/workbench app.

smarter.apps.llmclient.templatetags.react_llmclient_deployment.DEPLOYMENT_DATA_ID = 'smarter-llmclient-deployment-data'

The id of the json_script that holds the page’s data, an LLMClientDeploymentPage.

smarter.apps.llmclient.templatetags.react_llmclient_deployment.DEPLOYMENT_ROOT_ID = 'smarter-llmclient-deployment-root'

The id of the app’s root element.

smarter.apps.llmclient.templatetags.react_llmclient_deployment.llmclient_deployment_react_assets()[source]

Load CSS and JS files for a React app entry point based on its manifest.json.

Return type:

ReactAppAssets

smarter.apps.llmclient.templatetags.react_llmclient_deployment.templatetag_manager = <smarter.lib.django.templatetags.smarter_react_templatetag_manager.SmarterReactTemplateTagManager object>

Manages integration of Vite-built React assets into Django templates.

Expects to find a Vite-generated manifest.json in the file path static/react/@smarter/llmclient-deployment/.