AI
A model is optional and off until a superuser sets one up. Everything that uses it has an answer for when there is none, and what a model says comes back as a proposal: a person applies it, or doesn't.
Setting one up
In the admin UI, under AI:
| Provider | anthropic for Claude, or openai for anything that speaks the OpenAI chat shape: Ollama, LM Studio, llama.cpp’s server |
| Model | For Claude, claude-opus-5 unless you name another. Required for openai |
| Address | For openai, where it listens: http://localhost:11434/v1 for Ollama |
| Key | Kept on the server; never sent back out of the API |
| Effort | How hard Claude thinks: low, medium (the default) or high |
| Per person, per day | How many questions one person may ask; 0 for no limit. Administrators aren’t counted |
A model on the school’s own machine keeps everything on it.
In the browser
const { text } = await sluurp.ai.chat([{ role: "user", text: "Summarise this week's notices" }]);
// As it is written, piece by piece:
await sluurp.ai.stream(turns, { onText: (piece, all) => (out.textContent = all) });Both take { system, signal }; stopping with the signal keeps what came so far. When a person has asked their day’s share, the answer says so instead of throwing.
In server code
ai(system, prompt, schema) asks for an answer as JSON in the schema’s shape, or null when no model is set up:
const tags = await ai(
"You tag school notices.",
notice.body,
{ type: "object", properties: { tags: { type: "array", items: { type: "string" } } } },
);
if (tags) await collection("notices").update(notice.id, tags);For a model that works in turns, reading what it needs through tools until it has an answer, see Agents.
Where Sluurp uses it
Pages offers its AI tools, and sluurp/ai an assistant for any app, only when a model is set up.