Skip to content

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

@andrewshawcare

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

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions