Chatbot

Task-oriented recipes. Each section assumes you already have splam installed and running. See the Tutorial if not.

Read Explanation: Chatbot Scope 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:

.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:

.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 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 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 before choosing Option B on anything that isn’t your own workstation.

Option A: a local model with Ollama

Ollama 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.

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

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:

.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:

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
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:

%%{init: {'theme': 'dark', 'themeVariables': { 'fontFamily': 'monospace', "fontSize":"20px", "darkMode":true}}}%%
flowchart TD
    Start(["shiny run splam.app:app"])
    Extra{"chat extra<br>installed?"}
    NoTab["no Chat tab,<br>no model code runs"]
    Build[["build client<br>for the provider"]]
    Reach{"provider<br>answers?"}
    Help["Chat tab shows<br>setup help"]
    Box["Chat tab shows<br>a chat box"]

    Start --> Extra
    Extra -->|"no"| NoTab
    Extra ==>|"yes"| Build
    Build --> Reach
    Reach -->|"no"| Help
    Reach ==>|"yes"| Box

    style Start fill:#4CBB9D,color:#FFFFFF,rx:5,ry:5
    style Extra fill:#4CBB9D,color:#FFFFFF
    style Reach fill:#4CBB9D,color:#FFFFFF
    style Build fill:#4CBB9D,color:#FFFFFF,rx:5,ry:5
    style Box fill:#4CBB9D,color:#FFFFFF,rx:5,ry:5
    style NoTab fill:#FFFFFF,color:#000000,stroke:#333,stroke-width:1px,rx:5,ry:5
    style Help fill:#FFFFFF,color:#000000,stroke:#333,stroke-width:1px,rx:5,ry:5

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:

%%{init: {'theme': 'dark', 'themeVariables': { 'fontFamily': 'monospace', "fontSize":"20px", "darkMode":true}}}%%
flowchart TD
    Notes[("admin-task notes<br>for selected services")]
    Question[/"what you typed"/]
    History[("this session's<br>earlier messages")]
    Model[["the model"]]
    Tools[["read-only tools:<br>get_status, get_logs,<br>get_service_info,<br>read_audit_log"]]
    Answer[\"streamed answer"\]

    Notes ==> Model
    Question ==> Model
    History ==> Model
    Model ==> Answer
    Model <-.->|"tools on"| Tools

    style Notes fill:#4CBB9D,color:#FFFFFF,rx:5,ry:5
    style Question fill:#4CBB9D,color:#FFFFFF,rx:5,ry:5
    style History fill:#4CBB9D,color:#FFFFFF,rx:5,ry:5
    style Model fill:#4CBB9D,color:#FFFFFF,rx:5,ry:5
    style Answer fill:#4CBB9D,color:#FFFFFF,rx:5,ry:5
    style Tools fill:#FFFFFF,color:#000000,stroke:#333,stroke-width:1px,rx:5,ry:5

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 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.

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:

SPLAM_CHAT_TOOLS=1 shiny run splam.app:app

That registers four read-only functions: get_status, get_logs, get_service_info, and read_audit_log. Ask “what’s in the last hundred lines for sshd” and it calls 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 is not on that list and will not be added. The model cannot start, stop, or restart anything. Why.

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.

Use it from a terminal instead

The same conversation is available without a browser:

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 rather than here. It reads the same two environment variables, and --service picks the unit whose notes get loaded:

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.

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