Skip to content

feat(models): merge custom providers from opencode.json into model picker #392

Description

@DiegoT4l

Pre-flight Checklist

  • I have searched existing issues and this is not a duplicate
  • I understand that PRs will be rejected if the linked issue does not have status:approved

🔍 Affected Area

Agent Detection

💡 Problem Statement

The model picker screen (model_picker.go) loads providers exclusively from ~/.cache/opencode/models.json via LoadModels() and then filters them with DetectAvailableProviders(). This cache only contains OpenCode's built-in providers (Google, Anthropic, OpenCode Zen, etc.).

Custom providers defined in ~/.config/opencode/opencode.json under the "provider" key are completely ignored. This affects any user who has configured a custom provider — whether it's an OpenAI-compatible proxy, a self-hosted model, a LiteLLM gateway, or any third-party aggregator. For example:

{
  "provider": {
    "my-provider": {
      "name": "My Custom Provider",
      "npm": "@ai-sdk/openai-compatible",
      "options": { "baseURL": "https://my-api.example.com/v1" },
      "models": {
        "model-a": { "name": "Model A" },
        "model-b": { "name": "Model B" }
      }
    }
  }
}

When this user runs gentle-ai to configure SDD agents, the model picker only shows the built-in providers. None of the models from "my-provider" appear. This forces users to manually edit opencode.json after running gentle-ai to point agents to their custom provider's models, which defeats the purpose of having an interactive installer.

📦 Proposed Solution

In internal/opencode/models.go, after loading providers from the cache (LoadModels()), also parse the "provider" section from opencode.json (using DefaultSettingsPath()) and merge those custom providers into the map before calling DetectAvailableProviders().

Key changes:

  1. New function LoadCustomProviders(settingsPath string) (map[string]Provider, error) — reads opencode.json, extracts the "provider" object, and converts each entry to a Provider struct. Since custom providers use the @ai-sdk/openai-compatible format (with npm, options.baseURL, and a models map), the model entries typically only have name — so tool_call should default to true for all custom models (or be inferred).

  2. Merge step in LoadModels() or in the caller (model_picker.go / NewModelPickerState): combine cache providers + custom providers, with custom providers taking precedence on ID collision.

  3. Detection logic: For custom providers, since they don't have env vars defined in the same way as built-in providers, they should be considered "available" if they exist in opencode.json (the user has explicitly configured them, so they're authenticated by definition).

Expected behavior after the fix:

  • gentle-ai shows both built-in providers AND any custom providers from opencode.json
  • Users can select models from their custom provider directly in the TUI
  • No manual editing needed after running gentle-ai

🔄 Alternatives Considered

  1. Manual config only — current workaround: users run gentle-ai, then manually edit opencode.json to change model values to <custom-provider>/<model-id>. This defeats the purpose of having an interactive installer.

  2. CLI flag — add --provider flag to specify a custom provider ID. This adds friction and doesn't solve the discovery problem (users still need to know the exact model IDs).

  3. Read OpenCode's runtime model list — hook into OpenCode's internal model resolution instead of the cache file. This would be more accurate but requires deeper integration with OpenCode's internals.

📎 Additional Context

Relevant files:

  • internal/opencode/models.goLoadModels(), DetectAvailableProviders() (lines 69-157)
  • internal/tui/screens/model_picker.goNewModelPickerState() (line 58 calls LoadModels)
  • internal/opencode/models.go:20-27DefaultSettingsPath() already exists but is unused for provider loading

The DefaultSettingsPath() function already points to ~/.config/opencode/opencode.json, so the infrastructure to read the file is partially there — it just needs to be wired up for provider discovery.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions