Mentu

Local models and shell

Local models and shell

Two ways to keep a step off the network entirely.

Ollama

The ollama backend posts to http://localhost:11434/v1/chat/completions with the OpenAI-compatible chat schema. The host and port are fixed; point a custom provider at any other address. Its default model is llama3.2. Set the model you actually pulled.

{
  "label": "classify",
  "backend": "ollama",
  "model": "llama3.2",
  "prompt": "Classify this changelog entry as feature, fix, or chore. Answer with one word, then print CLASSIFY_COMPLETE.",
  "completion_keyword": "CLASSIFY_COMPLETE",
  "timeout": 120
}

No credential is needed for a default local server. If yours requires one, set OLLAMA_API_KEY in the environment or store it in the vault as ollama-api-key. The bearer header is sent only when a key resolves.

ollama always reports available in the mentu-recipes adapters table. The check asks for no credential and does not probe the server, so that row says nothing about whether Ollama is running. A step against a stopped server fails when it makes the request, not before it starts.

Any other local server

For a local server that is not Ollama, define a custom provider pointed at it:

{
  "providers": {
    "local-llm": {
      "api": "chat_completions",
      "base_url": "http://127.0.0.1:8080/v1",
      "api_key_env": "LOCAL_LLM_API_KEY",
      "model": "your-model"
    }
  }
}

Anything speaking the OpenAI chat-completions or Responses schema works. Unlike the built-in ollama backend, a custom provider always requires a credential to resolve, so export LOCAL_LLM_API_KEY with any non-empty value even if the server ignores it. See Cloud APIs for the provider fields.

Shell

The shell backend runs the step's prompt as /bin/sh -c <prompt> in the workspace. There is no model in the loop and no credential, and the runner itself opens no connection. The command can still do anything your shell can, including reaching the network.

{
  "label": "test",
  "backend": "shell",
  "dir": "app",
  "prompt": "swift test && echo TEST_COMPLETE",
  "completion_keyword": "TEST_COMPLETE",
  "timeout": 600
}

A shell step completes on exit code zero. Adding completion_keyword layers a text check on top: the keyword must appear in stdout or stderr as well.

A shell step receives your full process environment merged with the recipe and step env blocks. It is not allow-listed the way the CLI agents are.

dir runs the command in a subdirectory. It must be relative, must not start with ~, and must stay inside the selected workspace.

Shell is never selected automatically. A step gets one only by naming it, which is why mentu-recipes adapters marks it explicit. That is a deliberate boundary: an auto-detected shell would turn any prompt into a local command.

Treat recipes with shell steps as code. Review third-party recipes before you run them. doctor reports destructive_shell for commands matching known destructive patterns, but it is a lint, not a sandbox.

© 2026 Mentu.