LLM providers and character controllers

Bunnyland can use Ollama or OpenRouter for world generation and LLM-controlled characters. Provider access is optional: deterministic, scripted, behavioral, suspended, Discord, and MCP controllers do not require a model call.

Prerequisites

Complete the world and persistence guide. Decide separately:

Install the LLM extra in the same environment as the service:

uv sync --locked --extra server --extra llm

Keep credentials out of commands and config history

For Ollama Cloud:

OLLAMA_CLOUD_API_KEY_FILE=/etc/bunnyland/ollama.key
OLLAMA_HOST=https://ollama.com

For OpenRouter:

OPENROUTER_API_KEY_FILE=/etc/bunnyland/openrouter.key

Each key file should be owned by the service account and mode 0600. Never expose a local Ollama listener publicly. Do not put keys in world seeds, controller prompts, browser config, logs, screenshots, or shell command arguments.

Select providers and models

Ollama example:

uv run bunnyland serve \
  --llm \
  --llm-provider ollama \
  --worldgen-provider ollama \
  --worldgen-model deepseek-v4-pro \
  --character-model deepseek-v4-flash \
  --generator recursive \
  --max-rooms 6 \
  --save data/worlds/main.json

OpenRouter example:

uv run bunnyland serve \
  --llm \
  --llm-provider openrouter \
  --worldgen-provider openrouter \
  --worldgen-model PROVIDER/WORLDGEN_MODEL \
  --character-model PROVIDER/CHARACTER_MODEL \
  --generator recursive \
  --max-rooms 6 \
  --save data/worlds/main.json

Model names change more often than Bunnyland's CLI. Use a model currently available to your provider account and test structured tool calling before inviting players. World generation can use a stronger model than routine character turns. Loading an existing world does not need the world-generation provider, but LLM-controlled characters still need their configured character provider.

Choose controllers by role

Use cheap deterministic controllers for background population and reserve LLM controllers for characters whose judgment materially improves play. Controller handoff changes who proposes actions; it does not bypass normal command validation.

When persistent behavior/script definitions are loaded by editors, store them with --controller-definitions and include the file in the backup set.

Control cost and failure impact

Character and world prompts may contain community-authored content. Review the provider's retention and data-use terms and disclose the integration to players.

Verify before long-running use

Start a disposable world with one tick and verbose logging:

uv run bunnyland serve \
  --llm \
  --llm-provider openrouter \
  --worldgen-provider openrouter \
  --worldgen-model PROVIDER/WORLDGEN_MODEL \
  --character-model PROVIDER/CHARACTER_MODEL \
  --generator recursive \
  --max-rooms 2 \
  --ticks 1 \
  --verbose

Confirm generation, a native structured tool call, normal command validation, and provider usage reporting. Do not use production world paths for this test.

Troubleshooting

Bunnyland says a provider key is required

Confirm the matching literal or _FILE environment variable is visible to the service user. Do not set both forms. Check file permissions from the service account, not only from your interactive shell.

A provider returns prose instead of a tool call

Use a model with reliable native structured tool calling. Keep rejection/retry protection enabled and inspect bounded verbose logs without enabling prompt-content capture in production.

Generation works but character turns fail

Worldgen and character providers/models can differ. Verify --llm-provider and --character-model, and confirm the character provider key remains valid after startup.

Costs rise unexpectedly

Reduce LLM-controlled population, move background actors to behavioral/scripted controllers, lower generation room budgets, and inspect provider/model labels in telemetry. Do not solve a budget issue by disabling command safety or authentication.

← Worlds, plugins, persistence, and snapshots · Discord bot →