# Chatbot

Task-oriented recipes. Each section assumes you already have `splam` installed and running. See the [Tutorial](tutorial-getting-started.md) if not.

Read [Explanation: Chatbot Scope](02.04-explanation-chatbot-scope.md) alongside this page before enabling anything here, especially if your deployment handles regulated data.


# Add the Chat panel

The chatbot is optional and `splam` never imports it on its own:

``` bash
.venv/bin/pip install -e ".[chat]"
```

That pulls in `chatlas`, and it's the only new package involved. The chat widget itself is `shinychat`, which `shiny` already depends on, so it's in your environment whether you use it or not:

``` bash
.venv/bin/pip show shinychat | grep Required-by
```

Restart the app and a **Chat** tab appears after **Audit Trail**, in the same tab strip as **Status**, **Tasks**, and **Logs**. Skip the extra and there's no Chat tab, no network access, and no API key anywhere on the host.

If the extra is installed but no model provider can be reached, the tab still appears. Instead of a chat box it shows what went wrong and how to finish the setup, and the app prints the same thing on startup:

    splam: Chat not configured (Can't find locally running ollama.)

Whether that check happens at startup depends on the provider. Ollama, OpenAI, and Google all verify themselves when the app starts, so the panel reports the problem before you type anything. Anthropic doesn't: it accepts a missing or wrong key at startup, so you get a working-looking chat box and the first message is what fails.


# Point it at a model

`chatlas` talks to several providers. Two environment variables choose which, both read once when the app starts:

| Setting | Default | Behavior |
|----|----|----|
| `SPLAM_CHAT_PROVIDER` | `ollama` | One of `ollama`, `anthropic`, `openai`, `google` |
| `SPLAM_CHAT_MODEL` | `llama3.2` for `ollama`, otherwise the provider's own default | The model name passed straight through |

There are two ways to satisfy that, set out in full below. Pick one:

- [Option A: a local model](#option-a-a-local-model-with-ollama) keeps every log line on this host. It needs a model server installed and a few GB of disk, and answers are only as good as a model you can run locally.
- [Option B: a hosted provider](#option-b-a-hosted-provider) needs no local install and gives better answers. It also sends whatever the panel sees to a third party, which on a regulated host is a decision with a paper trail attached.

The default is Option A on purpose. Read [what leaves the machine](02.04-explanation-chatbot-scope.md#what-leaves-the-machine) before choosing Option B on anything that isn't your own workstation.


## Option A: a local model with Ollama

[Ollama](https://ollama.com) serves models from `localhost`. Nothing the panel sends reaches the network. Installing it has hardware requirements and puts a service, a system user, and a few GB of models on the machine, so it gets its own page: [How-To: Set Up Ollama](01.08-how-to-set-up-ollama.md).

Once it's running, there is nothing here to configure. `ollama` is already the default provider and `llama3.2` the default model:

``` bash
shiny run splam.app:app
```

No further pip install is required for this path. `chatlas` reaches Ollama through the `openai` client library, which it already depends on, so the `chat` extra alone is enough.


## Option B: a hosted provider

Each hosted provider needs its client library and an API key. The key is read by the provider's own SDK, not by `splam`:

| `SPLAM_CHAT_PROVIDER` | Install | API key variable |
|----|----|----|
| `anthropic` | `pip install "chatlas[anthropic]"` | `ANTHROPIC_API_KEY` |
| `openai` | already installed with `chatlas` | `OPENAI_API_KEY` |
| `google` | `pip install "chatlas[google]"` | `GOOGLE_API_KEY` |

Install the client, export the key, and start the app with the provider named:

``` bash
.venv/bin/pip install "chatlas[anthropic]"
export ANTHROPIC_API_KEY=...
SPLAM_CHAT_PROVIDER=anthropic shiny run splam.app:app
```

Add `SPLAM_CHAT_MODEL` to pin a specific model rather than the provider's default:

``` bash
SPLAM_CHAT_PROVIDER=anthropic SPLAM_CHAT_MODEL=claude-sonnet-4-5 shiny run splam.app:app
```

Keep the key out of the shell history and out of the repository. A key exported in an interactive shell is visible to anything else running as that user, so on a shared host prefer a systemd unit's `EnvironmentFile=` pointing at a `0600` file.


## Confirm it worked

A working provider gives you a chat box in the **Chat** tab. A broken one gives you the same tab showing the error and the two setup options, and prints the reason on startup prefixed `splam: Chat not configured`:

| What the tab says | What it means |
|----|----|
| `Can't find locally running ollama.` | Option A, but the Ollama service isn't running. Start it with `systemctl start ollama` |
| `Missing credentials` (OpenAI) or `No API key was provided` (Google) | Option B, but the key variable wasn't set in the environment the app was started from |
| `unknown chat provider: ...` | `SPLAM_CHAT_PROVIDER` is outside the four names above |
| No **Chat** tab at all | The `chat` extra isn't installed. See [Add the Chat panel](#add-the-chat-panel) |
| A chat box, then the first message errors | Anthropic with a missing or invalid `ANTHROPIC_API_KEY`. It's the one provider that doesn't check at startup |

The quickest way to prove the whole path end to end is to select a service, open **Chat**, and ask something only the notes can answer, such as "what's the compliance note for this service?" An answer that quotes your own wording means the system prompt, the provider, and the streaming are all working.

Both checks happen at startup, in this order, which is why a missing extra and a missing key look nothing alike:


*\[Rich HTML output -- view on the documentation site\]*


Anthropic is the exception to the second diamond. It answers "yes" at startup whether or not the key is valid, and fails on the first message instead.


# Ask it about the selected service

Open **Chat**, type a question, get a streamed answer. The panel is seeded with a system prompt built from the admin-task notes for whichever unit the sidebar's **Service** dropdown is on, the same entries the **Tasks** tab shows. Ask about `cron.service` and you get an answer shaped by the compliance note you wrote, not generic advice about cron.

Changing the **Service** dropdown mid-conversation does not clear the thread. The notes for the newly selected unit are added to the context, so you can compare two services in one conversation.

Every message carries the same three things. The fourth path opens only when `SPLAM_CHAT_TOOLS=1`, described in [Let it read logs on its own](#let-it-read-logs-on-its-own):


*\[Rich HTML output -- view on the documentation site\]*


Nothing else is in there. The panel does not read the **Logs** tab you happen to have open, and it does not see other users' sessions.


# Know who can see it

The **Chat** tab is visible to both `admin` and `auditor` accounts, unlike **Actions**. Reading and asking questions is what an auditor account is for, and nothing in the panel can change a service. See [Manage Logins](01.01-how-to-logins.md) for assigning roles.

Typing in the panel counts as session activity, so a long conversation won't trip the idle timeout described in [Explanation: Login Sessions](02.01-explanation-login-sessions.md).


# Let it read logs on its own

By default the model sees the selected service's notes and whatever you type. Turn on tools and it can fetch for itself:

``` bash
SPLAM_CHAT_TOOLS=1 shiny run splam.app:app
```

That registers four read-only functions: [get_status](../reference/get_status.md#splam.get_status), [get_logs](../reference/get_logs.md#splam.get_logs), [get_service_info](../reference/get_service_info.md#splam.get_service_info), and [read_audit_log](../reference/read_audit_log.md#splam.read_audit_log). Ask "what's in the last hundred lines for sshd" and it calls [get_logs](../reference/get_logs.md#splam.get_logs) rather than waiting for you to paste.

Calls are not individually approved. Setting the variable is the approval, granted once for the session, and each call and its result are shown in the conversation as they happen. Leave the variable unset and the model reads only what you type, which is the right default if you're pointed at a hosted provider.

[systemctl_action](../reference/systemctl_action.md#splam.systemctl_action) is not on that list and will not be added. The model cannot start, stop, or restart anything. [Why](02.04-explanation-chatbot-scope.md#why-it-reads-but-never-acts).


# Keep a transcript

**Export conversation** in the panel writes the whole thread to Markdown, via `chatlas`'s `.export()`. Useful when the conversation informed a change you'll have to justify later.

The transcript is a file you chose to save. It is not part of the audit trail, and a chat session on its own leaves no entry in `audit_log.jsonl`, because nothing was done to a service. See [Explanation: Audit Trail Scope](02.03-explanation-audit-trail-scope.md).


# Use it from a terminal instead

The same conversation is available without a browser:

``` bash
splam-chat
```

You get a prompt. Type a question, get an answer, repeat. Type `exit` or press `Ctrl+C` to leave. This is `chatlas`'s own `.console()`, so its behavior is documented [upstream](https://posit-dev.github.io/chatlas/get-started/chatbots.html) rather than here. It reads the same two environment variables, and `--service` picks the unit whose notes get loaded:

``` bash
splam-chat --service cron.service
```

If no provider is reachable it reports the same problem the panel does, rather than a traceback:

    splam-chat: chat is not configured (Can't find locally running ollama.)

Reach for it when there's no browser on the host, or when you're already in a terminal and don't want to log in for one question. Otherwise prefer the panel: the console has no login step, so it's bounded by the file permissions of whoever runs it rather than by a `splam` role, and it doesn't know which service you have selected. On a host where the `admin` and `auditor` split matters, don't treat the console as a read-only account. [Why the panel came first](02.04-explanation-chatbot-scope.md#why-a-panel-rather-than-a-terminal).


# Turn it off for everyone

Don't install the extra. With `chatlas` absent there's no **Chat** tab, no `splam-chat` on the `PATH`, and no code path that reaches a model provider. For a locked-down deployment that's the whole control: leave it out of the install, and confirm it with one `pip list`.


# Further reading

- [Chatbots in Shiny for Python](https://shiny.posit.co/py/docs/genai-chatbots.html), which the panel is built from
- [Chatbots in chatlas](https://posit-dev.github.io/chatlas/get-started/chatbots.html), which documents the console the `splam-chat` command wraps
- [Ollama](https://ollama.com) and its [model library](https://ollama.com/library), for local models beyond the two named here
