Skip to content

[FEATURE]: Add a native V2 replacement for provider model filtering #49986

Open

Description

@geril07
  • I have verified this feature I'm about to request hasn't been suggested before.

Describe the enhancement

OpenCode V1 supports per-provider model filtering with whitelist and blacklist:

{
  "provider": {
    "ollama-cloud": {
      "whitelist": ["glm-5.2"]
    }
  }
}

This is useful when a provider exposes a large or changing catalog.

V2 currently drops these fields during V1-to-V2 normalization. The provider remains available, but the model filter is not applied.

Reproduction

Tested with @opencode/[email protected] using a clean config and:

npx --yes @opencode/[email protected] \
  api --standalone --print-logs get /api/config

The normalized config contains the provider settings but omits whitelist. The server log reports:

path=$.provider.ollama-cloud.whitelist
kind=unsupported
action="omitted unsupported legacy setting"

Expected behavior

Please either:

  1. provide a native V2 equivalent for per-provider model filtering; or
  2. document the intentional removal and recommended replacement clearly in the V2 migration guide.

The old provider documentation still documents this feature, which makes migration unclear.

References

Related: #42535 covers wildcard support for the existing V1 filter syntax. This issue concerns the missing V2 migration path.

Activity

  1. GaboEI commented on Sep 29, 2026

    @GaboEI

    I built a community workaround for this exact V2 migration problem:

    https://github.com/GaboEI/opencode-model-filters-v2

    It is an OpenCode V2 plugin that restores per-provider whitelist and blacklist filtering through the V2 model transform API. It does not patch, replace, or pin the OpenCode binary.

    Behavior:

    • no filter configured: all discovered models remain visible, including new ones;
    • blacklist: only listed IDs are hidden, so newly added provider models appear automatically;
    • whitelist: only listed IDs are shown, so new models require adding their IDs;
    • configuration remains in the normal opencode.json.

    The repository includes installation instructions, tests, and an MIT license. This is a community workaround while a native V2 replacement is being discussed.

  2. cleardevice commented on Oct 10, 2026

    @cleardevice

    The V2 provider docs ("Hiding models" in providers.mdx) still describe whitelist and blacklist, while normalization drops both with "omitted unsupported legacy setting", so the documented config does nothing. I opened #54372 to make the documented behavior work: the V1 lists migrate to model overrides, with a new providers.<id>.models["*"] provider-wide override to express whitelist. Unlike the plugin workaround above, it applies to every config layer including .jsonc, follows config reloads, and an empty whitelist hides all models as in V1. Happy to change the V2 shape if you have a different design in mind.

  3. opencodejr commented on Oct 28, 2026

    @opencodejr
    Contributor

    Thanks for the detailed write-up! You're asking for a native V2 way to filter which models a provider exposes, the V2 equivalent of V1's whitelist/blacklist, or failing that, a documented replacement. Maintainers will consider it.

    Dropping these fields is intentional. The V2 migration guide lists them under Accepted but unsupported fields, but it doesn't say what to use instead, so that's a fair gap.

    Until then, you can get the same behaviour with a small V2 plugin using the ctx.model.transform hook. Save this as .opencode/plugins/model-filter.ts in your project, or ~/.config/opencode/plugins/model-filter.ts for all projects. Plugins there load automatically, so you don't need a config entry:

    import { Plugin } from "@opencode/plugin"
    
    // providerID -> filter. `whitelist` keeps only matching models; `blacklist` removes matching ones.
    // Patterns support `*` wildcards.
    const filters: Record<string, { whitelist?: string[]; blacklist?: string[] }> = {
      "ollama-cloud": { whitelist: ["glm-5.2"] },
    }
    
    const match = (patterns: string[], id: string) =>
      patterns.some((p) =>
        new RegExp("^" + p.split("*").map((s) => s.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join(".*") + "$").test(id),
      )
    
    export default Plugin.define({
      id: "model-filter",
      async setup(ctx) {
        await ctx.model.transform((editor) => {
          for (const [providerID, filter] of Object.entries(filters)) {
            for (const model of editor.list(providerID)) {
              const allowed = filter.whitelist ? match(filter.whitelist, model.id) : true
              const blocked = filter.blacklist ? match(filter.blacklist, model.id) : false
              if (!allowed || blocked) editor.remove(providerID, model.id)
            }
          }
        })
      },
    })

    Edit filters to suit your providers. It also supports * wildcards, which #42535 asks for. I've checked that it type-checks against the plugin API but haven't run it against a live provider. Details on the hook are in the plugins guide.

    Related:

  4. sleetdrop commented on Nov 4, 2026

    @sleetdrop

    +1 from an enterprise governance angle: we run an internal OpenAI-compatible gateway
    that serves internal and third-party models under a single custom provider, and
    projects need per-model boundaries ("this project may only use the internal models").

    Today we maintain, per project, a "virtual" custom provider that re-declares only the
    approved models (same baseURL/credential) and allow it via provider.use — fail-closed
    and verified on v2.2.5, but it duplicates endpoint/credential/model definitions in
    every project.

    The models["*"] wildcard in #54372 would remove most of that duplication at the
    config layer. We've also opened #61601 for the policy-level angle (model.use, or
    provider.use with provider/model resources), since policies carry the
    managed-config enforcement semantics that ordinary settings don't. Either path solves
    our case; happy to help validate.

  5. cleardevice commented on Nov 4, 2026

    @cleardevice

    Thanks for the detailed use case! If you get a chance to validate: #54372 is not merged yet, so trying it means building from the PR branch. For your case, the gateway provider keeps its shared baseURL/credential and model list in the global config, and each project narrows it in its own config instead of declaring a virtual provider:

    // project opencode.jsonc
    {
      "providers": {
        "internal-gateway": {
          "models": {
            "*": { "disabled": true },
            "internal-model-a": { "disabled": false }
          }
        }
      }
    }

    "*" is a provider-wide default for disabled, not a pattern: approved models are listed by exact ID, and models added to the gateway later stay hidden in that project. Feedback on whether this removes the duplication in practice, or where it falls short, would help the maintainers judge the design. It is still an ordinary setting that another config layer can override rather than an enforced policy, so #61601 covers a separate need.

  6. sleetdrop commented on Nov 10, 2026

    @sleetdrop

    Follow-up with a reproduction: the ctx.model.transform + editor.remove() pattern recommended above is a
    silent no-op on 2.2.5 — the transform fires and the editor reflects the removals, but the catalog and
    --model resolution are unaffected. Details and a minimal repro: #63100

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions