Skip to content

[feature]: Generic OpenAI-compatible gateway quota provider #109

Description

@andrewshawcare

Pre-flight checks

  • I searched existing issues and did not find a duplicate request.
  • I reviewed current behavior on the production released OpenCode version.

OpenCode version reviewed

1.15.12

Problem statement

Every provider hardcodes its vendor's balance/quota URL in src/lib/<vendor>.ts, and createProviderApiKeyResolver reads only an API key, not a base URL. So self-hosted / OpenAI-compatible gateways (LiteLLM proxies, in-house or university gateways, Vertex/Apigee fronts, OpenRouter-compatible endpoints) can't be supported, even though users already configure them as ordinary OpenAI-compatible providers for chat.

There's no standard remaining-quota endpoint, so each such gateway publishes its own small JSON but the plugin currently has no way to point at one.

Proposed change

A single, config-driven generic provider (openai-compatible) that polls a configurable quota endpoint and maps a small JSON shape into the normalized entries. One provider covers any number of gateways:

"experimental": {
  "quotaToast": {
    "openaiCompatibleGateways": [
      { "providerId": "my-gateway", "quotaPath": "/quota" }
      // baseURL inherited from provider.<id>.options.baseURL; apiKey via the existing resolver
    ]
  }
}

Key resolved via the existing createProviderApiKeyResolver (env / trusted global config / auth.json), keyed on providerId.

GET <baseURL><quotaPath> (Bearer) → a default vendor-neutral shape:

  {
    key,
    tokens: {
      limit,
      used,
      remaining,
      resets_at
    },
    cost: {
      currency,
      limit,
      used,
      remaining
    }
  }

with a built-in openrouter preset mapping (data:{usage,limit,limit_remaining}) so OpenRouter-style endpoints work too.

Token bucket → percent entry; cost → value entries. Bound to no product.

I'd start from contributing/provider-template/ and follow the existing resolver/result-helper patterns.

Alternatives considered

  1. Hardcode OpenRouter (add an openrouter.ts): narrower (one product) and binds to a product API rather than the open OpenAI-compatible class.
  2. Read a marker off provider.<id>.options instead of plugin config: avoids new plugin-config surface, but risks OpenCode rejecting unknown options keys and couples to the chat provider block.

Acceptance criteria

  • openai-compatible provider registered; config-driven gateways list validated like other settings.
  • Default neutral shape + openrouter preset mapping, both unit-tested (env/config/auth key sources covered).
  • pnpm run typecheck && pnpm test && pnpm run build green; started from contributing/provider-template/.
  • README documents setup; /quota_status reports the provider's key source.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions