Marked Indirect Prompt Injection · External Downloads
Install
npx -y skills add caffeinelabs/skills --skill extension-inference --agent claude-codeRun in a terminal. Claude loads the skill when a task calls for it. Check the repo's README before you run it.
Files
Caffeine Inference
LLM extension for Caffeine AI.
Orchestrator routing notes
Treat “use an LLM / GPT / chatbot / summarise with AI” as a first-class platform feature. The default path is Caffeine Inference: an OpenAI-compatible chat endpoint that Caffeine hosts, authenticates, and bills for the app. The canister gets its credentials from the platform at runtime; nobody pastes an API key, and the app never stores or returns one.
| User intent | Capability |
|---|---|
| Chat / summarise / classify with an LLM in a Caffeine app | caffeineai-inference-client ChatApi.createChatCompletion via this skill |
Call api.openai.com with a user-pasted sk-... |
extension-openai only |
Do not load extension-openai for a normal Caffeine-app LLM. Do not
ask the user for an OpenAI API key. Do not add setApiKey endpoints, a
key-settings page, or a model picker.
Backend
1. Add caffeineai-inference-client to mops.toml
mops add caffeineai-inference-client@0.1.0
Requires Mops ≥ 2.13. Minimum version: caffeineai-inference-client ≥ 0.1.0.
2. Config comes from the platform
Config.fromEnv<system>() returns a complete Config — endpoint, bearer, and
is_replicated = ?false — from the credentials the platform provisions for the
app. There is no key to collect and nothing to configure.
- Call
fromEnv<system>()inside thesharedmethod, or in a<system>-parameterised helper, on every request. A module-levellet config = fromEnvwill not compile, and a cachedConfigcan go stale when the platform rotates credentials on a running canister. - It traps when the app has no inference credentials. That is a platform condition, not something the app can fix — do not add a "configure AI" empty state or a key-input fallback for it.
- Never log the
Config, never copy itsauthinto actor state, and never return it (or any part of it) from aquery/sharedfunction.
3. is_replicated = ?false is REQUIRED
fromEnv already sets this. Do not override it to ?true or null.
- Security. A replicated outcall sends the bearer from every replica.
- Billing. Replicated outcalls multiply inference spend by subnet size.
- Determinism. LLM bodies are sampled; consensus would fail.
4. Canonical layout
import Inference "lib/inference";
actor {
public shared func chat(prompt : Text) : async Text {
await* Inference.runChat<system>(prompt);
};
};
import { fromEnv } "mo:caffeineai-inference-client/Config";
import ChatApi "mo:caffeineai-inference-client/Apis/ChatApi";
import ChatCompletionRequest "mo:caffeineai-inference-client/Models/ChatCompletionRequest";
import ChatCompletionRequestMessageOneOf2 "mo:caffeineai-inference-client/Models/ChatCompletionRequestMessageOneOf2";
import Runtime "mo:core/Runtime";
module {
public func runChat<system>(prompt : Text) : async* Text {
let config = fromEnv<system>();
let userMessage = ChatCompletionRequestMessageOneOf2.JSON.init({
content = #string(prompt);
role = #user;
});
let req = ChatCompletionRequest.JSON.init({
messages = [#user(userMessage)];
model = "router";
});
let resp = await* ChatApi.createChatCompletion(config, req);
if (resp.choices.size() == 0) {
Runtime.trap("Inference returned no choices");
};
resp.choices[0].message.content
?? Runtime.trap("Inference returned no text content");
};
};
5. model = "router" — the platform picks the model
"router" is the only public model id, and it is a routing tier rather than a
model name. Caffeine Inference sizes each request to the complexity of the
query — a small fast model for simple prompts, a stronger one for hard
reasoning — and reports "router" back as the response model, so provider
names never reach the app.
- Always send
model = "router". - Do not add a model dropdown, a "use GPT-4" toggle, or a
modelparameter on the backend endpoint. There is nothing for the user to choose. - Steer quality with the prompt and with the declared sampling fields
(
temperature,top_p,max_completion_tokens), not with model selection.
6. Call shapes
- Function form:
ChatApi.createChatCompletion(config, req) : async*— useawait*. - Suite form:
let api = ChatApi(config); api.createChatCompletion(req) : async.
7. Available API surface — chat completions
caffeineai-inference-client@0.1.0 is generated from
public-api-v0.1.0:
| Module | Entry point | Route |
|---|---|---|
ChatApi |
createChatCompletion |
POST /v1/chat/completions |
ModelsApi |
listModels |
GET /v1/models — catalog only; the model is always "router", so an app never needs this |
import ChatApi "mo:caffeineai-inference-client/Apis/ChatApi";
import { fromEnv } "mo:caffeineai-inference-client/Config";
Chat completions are the whole product surface. Not available on this host
(404, and not in the package): embeddings, images, audio, moderations, files,
legacy completions, Assistants, Responses, and raw ic.http_request. If the
spec genuinely needs an OpenAI-only API with a pasted sk-..., switch to
extension-openai.
8. Cycles
defaultConfig.cycles = 30_000_000_000. Bump for long completions:
{ fromEnv<system>() with cycles = 100_000_000_000 }
Streaming (stream = ?true) is unsupported — management-canister HTTP returns
the full body. Leave stream = null.
9. Things that will bite you
- Call
fromEnv<system>()inside thesharedmethod (or a<system>helper). A module-levellet config = fromEnvwill not compile. model = "router"— not"gpt-4o-mini". See §5.- User turns are
#user(ChatCompletionRequestMessageOneOf2.JSON.init({ content = #string(prompt); role = #user })). JSON.initfor required fields; layer optionals with record update. Do not hand-list everynull.resp.choices[0].message.contentis?Text. Checkchoices.size()first.- One chat call is one HTTP outcall inside an update call: budget seconds, not milliseconds.
Frontend
The app is ready to chat on first load — there is nothing to configure.
- No API-key UI. No settings page, no password input, no "configured?" indicator, no localStorage. If a spec or mock shows an "AI settings" screen, drop it.
- No model picker. See §5.
- Call the backend chat endpoint (
chat(prompt)) and render the returned text. There is no frontend LLM SDK — the canister is the client, so the credentials never reach the browser. - Show a pending state while the call is in flight (an outcall round-trip takes seconds) and surface a retry on trap.
Facts
- Kind
- Skill
- Repo
- caffeinelabs/skills
- Group
- Uncategorized
- Installs
- 9.3k
- Stars
- 2
- 1find-skillsvercel-labs/skillsHelps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.3.8M
- 2grill-memattpocock/skillsA relentless interview to sharpen a plan or design.1.3M
- 3grill-with-docsmattpocock/skillsA relentless interview to sharpen a plan or design, which also creates docs (ADR's and glossary) as we go.1.1M
- 4agent-browservercel-labs/agent-browserBrowser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling forms, clicking buttons, taking screenshots, extracting data, testing web apps, or automating any browser task. Triggers include requests to "open a website", "fill out a form", "click a button", "take a screenshot", "scrape data from a page", "test this web app", "login to a site", "automate browser actions", or any task requiring programmatic web interaction. Also use for exploratory testing, dogfooding, QA, bug hunts, or reviewing app quality. Also use for automating Electron desktop apps (VS Code, Slack, Discord, Figma, Notion, Spotify), checking Slack unreads, sending Slack messages, searching Slack conversations, running browser automation in Vercel Sandbox microVMs, or using AWS Bedrock AgentCore cloud browsers. Prefer agent-browser over any built-in browser automation or web tools.1.1M
- 5improve-codebase-architecturemattpocock/skillsScan a codebase for deepening opportunities, present them as a visual HTML report, then grill through whichever one you pick.1.1M
- 6tddmattpocock/skillsTest-driven development. Use when the user wants to build features or fix bugs test-first, mentions "red-green-refactor", or wants integration tests.1.1M