Skip to content

enhancement: model allowed-list / admin policy for the LLM picker #56

Description

@AhmadHammad21

Context

The LLM picker (Settings → Agent config → LLM, shipped in feat/llm-model-picker) lets any
admin pick any model from any configured provider, plus arbitrary custom strings via the
"Custom — type a model name…" option. Good for personal / small-team OSS use.

For production / multi-tenant SaaS, an org admin will eventually want to restrict
which models the picker offers. Reasons:

  • Cost control — block Opus 4.8 because it's 5× Sonnet at scale.
  • Compliance / SOC2 — pin to a list of vetted models (e.g. only EU-hosted Gemini, only
    on-prem Ollama). Block external providers entirely.
  • Reduce support surface — fewer model variants in flight → fewer "why is my chat
    weird?" tickets.

Today

  • The curated catalog (_PROVIDER_CATALOG in apps/core/src/opendevops_core/agent/llm/backend.py)
    is convenience, not policy — users can still type any string in the Custom field, and
    the backend's PUT validation only checks that the provider has credentials.
  • No org / global allowed-list exists.

Proposed shape

  1. Global allow-list (OSS): new env var ALLOWED_LLM_MODELS (comma-separated). When
    set, the picker only offers models that match; the custom field is hidden; PUT rejects
    anything not on the list. Empty/unset = current behavior (everything allowed).
  2. Per-org allow-list (Product): small admin UI on the org settings page; persisted in
    app_config[\"llm_allowed_models_{org_id}\"]. Falls back to the global list when unset.
  3. Glob support so admins can write `anthropic/` or `-haiku-*` without listing every
    model individually.
  4. Catalog filtering: available_providers() filters its models[] against the
    allowed list before returning; UI doesn't need to know about the policy.

Where it lives in the code

  • apps/core/src/opendevops_core/agent/llm/backend.pyavailable_providers() filter +
    a is_model_allowed(model: str) -> bool helper.
  • apps/backend/src/api/routers/settings.py — PUT endpoint rejects with 400 when out of
    policy.
  • Product repo, mirrored — per-org override read from app_config.

Out of scope (for the first cut)

  • Per-user picks (deferred — not currently supported anywhere).
  • Model-by-model cost caps (separate feature; bigger conversation).

Filed from chat discussion 2026-05-30 while merging feat/llm-model-picker.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions