Skip to content

[Feature] Declarative "Custom Provider": bring-your-own endpoint + JSONPath field mapping #1735

Description

@lkangd

TL;DR

Let users define their own provider in config.json: a custom endpoint (URL + auth) plus a small JSONPath-style mapping that translates that provider's usage response into CodexBar's RateWindow / ProviderCostSnapshot / ProviderIdentitySnapshot fields — no Swift code, no fork, no release wait.

Why I'm filing this

I self-host an LLM gateway. CodexBar can't see it. And I'm far from alone — the issue tracker tells the story. Every few weeks someone files "please add my provider":

Each is real demand, and each — today — needs a whole Swift module: ProviderDescriptor + SettingsReader + UsageFetcher + ProviderImplementation + an SVG icon + localization. That's right for first-class providers. For the long tail of self-hosted / in-house / boutique gateways it's a bottleneck for everyone: the user can't see their quota, and the maintainer drowns in one-off integrations nobody can really maintain.

There's already a beautiful template: #264 → LLM Proxy (LLMProxyUsageFetcher) and LiteLLM. Both already take base URL + API key and hit a known schema. The only thing keeping them from being generic is that the endpoint path and field extraction are hardcoded in Swift (Decodable CodingKeys + a hand-written toUsageSnapshot()). Even the two quota_groups shapes (array vs keyed) are special-cased in code.

That hand-coded mapping is exactly the seam a declarative provider would fill.

What I'd love

A type: "custom" provider where a user supplies:

  1. The request — method, path, auth header/env, optional body. (Ask No rate limits event #1: "fill in a custom usage/quota query".)
  2. The mapping — JSONPath (or dot-path) expressions pointing into that provider's response, bound to CodexBar's existing model fields. (Ask Homebrew #2: "a return-data path transformer → usage / balance / resetsAt".)

Sketch

{
  "providers": {
    "acme-gateway": {
      "type": "custom",
      "label": "Acme Gateway",
      "icon": "proxy",                 // built-in fallback, or path to a user svg
      "baseURL": "https://gw.acme.io",
      "auth": { "header": "Authorization", "prefix": "Bearer ", "env": "ACME_GATEWAY_API_KEY" },
      "usage": { "method": "GET", "path": "/v1/quota" },
      "mapping": {
        "usedPercent":      "$.quota.used_pct",
        "remainingPercent": "$.quota.remaining_pct",
        "resetsAt":         "$.quota.reset_at",        // ISO-8601
        "windowMinutes":    "$.quota.window_min",
        "costUsed":         "$.spend.usd",
        "costCurrency":     "USD",
        "costPeriod":       "Approx. spend",
        "organization":     "$.plan.name",
        "loginMethod":      "$.credits.balance_str"
      }
    }
  }
}

Field binding (reuses types CodexBar already has)

CodexBar field Type Example path
RateWindow.usedPercent number 0–100 $.quota.used_pct
RateWindow.remainingPercent number 0–100 $.quota.remaining_pct
RateWindow.resetsAt ISO-8601 date $.quota.reset_at
RateWindow.windowMinutes number $.quota.window_min
ProviderCostSnapshot.used number $.spend.usd
ProviderIdentitySnapshot.accountOrganization string $.plan.name
ProviderIdentitySnapshot.loginMethod string $.credits.balance_str

Fill in only the fields the endpoint exposes; the rest stay empty, same as any provider with sparse data.

Why this is low-risk

  • Opt-in. Nothing happens until a type: "custom" entry exists. All 53 existing providers keep working unchanged.
  • Reuses the existing model. Output is the same UsageSnapshot the menu already renders — no new UI primitives.
  • Reuses existing security posture. Base-URL validation, keychain storage, and the HTTPS-only hardening already in place for LLM Proxy / LiteLLM / Azure apply directly.
  • Cuts maintainer load. The "add my provider" tail routes to a docs page instead of a Swift PR.

Alternatives considered

  • Fork + hand-write a provider each time. Works, but doesn't scale (one gateway = one fork to chase across releases) and helps nobody else.
  • Just use LLM Proxy / LiteLLM. Great if a gateway happens to mirror those exact schemas — most self-hosted ones don't, and reshaping a server's response to match CodexBar is backwards.

Open questions (happy to defer)

  • Expression engine. Full JSONPath, a dot-path subset, or jq-lite? I'd start with dot-path + a couple of coercion hints (ISO-8601 date, percent) and grow.
  • Refresh semantics. Expose POST/force-refresh like LLM Proxy, or keep custom providers poll-only?
  • Multi-window. Array of mappings for providers exposing session/weekly/monthly in one call (mirrors extraRateWindows).

Happy to prototype or draft the docs. 🙌

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Low-risk cleanup, docs, polish, ergonomics, or speculative feature.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.impact:auth-providerThis issue is about auth, provider routing, model choice, or SecretRef resolution.issue-rating: 🌊 off-meta tidepoolIssue quality rating does not apply to this item.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions