# `MishkaCmsCore.Runtime.PageBuilder.AiBackend`
[🔗](https://github.com/mishka-group/mishka_cms_core/blob/v0.0.1/lib/mishka_cms_core/runtime/page_builder/ai_backend.ex#L5)

The seam the Assistant rail talks to, and an honest default that does not pretend to be an AI.

The rail's UI is real: the conversation, the scope bound to the live selection, and per-message
**Restore** wired to genuine `EditorSession` history entries. What is stubbed is only the part
that would need a model — and `Echo` says so in its reply rather than fabricating an edit. A
suggestion the editor cannot actually make must not look like one it made.

    config :mishka_cms_core, :builder_ai_backend, MyApp.RealAssistant

A real backend returns the same `t:message/0`, optionally carrying `ops` — validated
`MishkaCmsCore.Runtime.PageBuilder.Op` structs the session applies through the ordinary funnel, so
an AI edit is undoable, containment-checked and plugin-visible exactly like a human one.

# `message`

```elixir
@type message() :: %{
  :role =&gt; :ai | :user,
  :text =&gt; String.t(),
  :plain =&gt; String.t() | nil,
  :scope =&gt; String.t() | nil,
  :ops =&gt; [map()],
  optional(:attachment) =&gt; term(),
  optional(:revision) =&gt; term(),
  optional(:revision_note) =&gt; term()
}
```

# `scope`

```elixir
@type scope() :: %{node_id: String.t() | nil, label: String.t()}
```

# `ask`

```elixir
@callback ask(String.t(), scope(), keyword()) :: {:ok, message()} | {:error, term()}
```

# `name`

```elixir
@callback name() :: String.t()
```

# `adapter`

```elixir
@spec adapter() :: module()
```

The configured backend.

# `adapter_note`

```elixir
@spec adapter_note() :: String.t()
```

One line about the configured backend, for the rail's Advanced settings panel.

# `ask`

```elixir
@spec ask(String.t(), scope(), keyword()) :: {:ok, message()} | {:error, term()}
```

Asks the configured backend for a change, scoped to the current selection.

`opts` names the document being edited — `:token`, `:section`, `:resource_id`, `:site_id` — so a
backend that would rather write its own ops than return them has the handle
`MishkaCmsCore.Runtime.PageBuilder.EditorSession` is addressed by.

# `connected?`

```elixir
@spec connected?() :: boolean()
```

Whether a real backend is configured.

The rail says so plainly either way. Claiming "Connected" while the default Echo is answering
would make its honest "no model is configured" reply read as a malfunction instead of the truth.

# `user_message`

```elixir
@spec user_message(String.t(), scope()) :: message()
```

A user's own message, for appending to the transcript before the backend answers.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
