Skip to content

Docs: Running the AI Planner for free via OpenRouter + LiteLLM proxy #966

Description

@datend3nker

Summary

The self-hosting docs (SelfHosting.md) mention AiPlanner__AnthropicApiKey as the only
configuration point for the AI planner, which implies Anthropic is the only usable provider.
It's actually possible to run the planner completely free using OpenRouter's free-tier
models, routed through a small LiteLLM proxy. Wanted to document this for others hitting the
same "I don't want to pay for Claude" question.

Why this works

LiftLog.Api constructs the Anthropic client as new AnthropicClient { ApiKey = apiKey }
(see RegistrationHelpers.cs). Since BaseUrl is never set explicitly, the underlying SDK
falls back to the ANTHROPIC_BASE_URL environment variable if present. That means you can
point the server at any endpoint that speaks Anthropic's /v1/messages format. This includes a
self-hosted LiteLLM proxy, which can translate that format to OpenRouter (or any other
provider) under the hood.

Setup

docker-compose.yml (added service alongside the LiftLog API container):

name: liftlog

services:
  liftlog:
    image: ghcr.io/liammorrow/liftlog:api
    container_name: liftlog
    restart: unless-stopped
    environment:
      Database__Provider: Sqlite
      Database__ConnectionString: "Data Source=/var/lib/liftlog/liftlog.db"
      Auth__ApiKey__Value: ${LIFTLOG_API_KEY}
      AiPlanner__AnthropicApiKey: ${LITELLM_MASTER_KEY}
      ANTHROPIC_BASE_URL: "http://litellm:4000"
    volumes:
      - liftlog_data:/var/lib/liftlog
    networks:
      - proxy
      - default
    depends_on:
      - litellm
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.liftlog-rtr.entrypoints=websecure"
      - "traefik.docker.network=proxy"
      - "traefik.http.routers.liftlog-rtr.rule=Host(`liftlog.example.com`)"
      - "traefik.http.routers.liftlog-rtr.tls=true"
      - "traefik.http.routers.liftlog-rtr.service=liftlog-svc"
      - "traefik.http.services.liftlog-svc.loadbalancer.server.port=8080"
      - "com.centurylinklabs.watchtower.enable=true"

  litellm:
    image: ghcr.io/berriai/litellm:main-stable
    container_name: liftlog-litellm
    restart: unless-stopped
    environment:
      LITELLM_MASTER_KEY: ${LITELLM_MASTER_KEY}
      OPENROUTER_API_KEY: ${OPENROUTER_API_KEY}
    volumes:
      - ~/docker/liftlog/litellm-config.yaml:/app/config.yaml:ro
    command: ["--config", "/app/config.yaml", "--port", "4000"]
    networks:
      - default

volumes:
  liftlog_data:

networks:
  default:
  proxy:
    external: true

litellm-config.yaml:

model_list:
  - model_name: claude-sonnet-4-6   # must match AiPlanner__AnthropicModelId (default)
    litellm_params:
      model: openrouter/nvidia/nemotron-3-ultra-550b-a55b:free
      api_key: os.environ/OPENROUTER_API_KEY
  - model_name: claude-sonnet-4-6-fallback-1
    litellm_params:
      model: openrouter/nvidia/nemotron-3-super-120b-a12b:free
      api_key: os.environ/OPENROUTER_API_KEY

router_settings:
  fallbacks:
    - claude-sonnet-4-6: ["claude-sonnet-4-6-fallback-1"]
  cooldown_time: 30

No code changes to LiftLog itself needed. Just the env var and the proxy container.

Caveats worth documenting

  • OpenRouter's free models are rate-limited (typically ~20 req/min, ~200/day) and occasionally
    return provider_unavailable under load. So LiteLLM's fallbacks config handles that
    gracefully by trying the next model.
  • Not all free models honour the planner's tool-calling JSON schema as reliably as Claude does;
    bigger reasoning models (e.g. Nemotron 3 Ultra) do better than smaller ones, but occasional
    malformed plans are more likely than with the real Anthropic API.
  • This is obviously best-effort/community knowledge, not something the project needs to support
    officially. I am just flagging it in case it's worth a line in SelfHosting.md or Backends.md for
    discoverability, since "can I avoid the Anthropic API cost" seems like a common question for
    self-hosters.

Happy to open a PR against the docs if that's welcome instead of just an issue.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions