Repository navigation
feat(usage): add DeepInfra quota provider - #3600
Conversation
Code Review SummaryFor the maintainer: Merge — a clean, pattern-following provider addition; only two small non-blocking polish items remain. The PR registers DeepInfra as a quota provider in all three layers: a web-server module ( Verdict: PASS Reviewed HEAD:
|
33fbbd5 to
023a10e
Compare
Code Review SummaryFor the maintainer: Don't merge yet. Since the last review the branch was rebased onto current main, and main has since removed the The prior review passed this PR at Verdict: BLOCKED Reviewed HEAD:
|
…vider fetchDeepinfraQuota called readAuthFile(), which does not exist in this module, so the VS Code type-check failed with TS2304 and the provider could never have read a key at runtime. Match the neighbouring Hyper provider: inject readAuth with readOpenCodeCredentials as the default and await it, since the credential reader may be asynchronous.
…k balance as missing
1d26029 to
5b126b7
Compare
|
Thanks! After the rebase the web provider still imported the old auth reader that main had removed, so I switched it to |
Code Review SummaryFor the maintainer: Merge after one refresh — all code findings from the last review are fixed at this HEAD, but the live-run statement and "after" screenshot still describe the pre-fix branch. Delta since the last review ( Verdict: NEEDS_EVIDENCE Reviewed HEAD:
|
Intent
DeepInfra is present in the OpenCode
auth.json(deepinfraentry with an API key) and its account exposes a spendable credit balance, but DeepInfra never appears in Settings → Usage, the work-status panel, or the VS Code runtime. The quota system is a hand-maintained provider allowlist, and DeepInfra was never registered there.This PR adds
deepinfraas a first-class quota provider: a DeepInfra row in Settings → Usage with acredits_balancecard, opt-in work-status panel visibility through the existing per-provider checkbox, and VS Code extension parity.The provider reads
GET https://api.deepinfra.com/v1/me?checklist=true(bearer API key) and surfaceschecklist.stripe_balanceas the spendable credit. Per the documented semantics,stripe_balanceis negative when funds are ready to spend and positive when money is owed, so the provider negates it before rendering the money label ($50.75for a livestripe_balanceof-50.75;-$5.50when a balance is owed). This matches the existing balance-only provider pattern (DeepSeek, Charm Hyper).Reference: https://docs.deepinfra.com/api-reference/account/me
Non-goals
GET /payment/usage; the balance-only card is the DeepSeek/Hyper pattern and keeps the fetch to one request.auth.jsonAPI key like DeepSeek and OpenRouter.models.devlogo (deepinfra.svgresolves today).Credits Balanceis an existingquota.window.creditsBalancelabel andDeepInfrais a product-name literal.usage.models): the DeepInfra account endpoint has no per-model quota data.Affected surfaces
packages/webserver quota moduleserver/lib/quota/providers/deepinfra.js, one registry entry and named export inproviders/index.js, new vitest suitepackages/uishared types and registryQuotaProviderIdunion member and oneQUOTA_PROVIDERSentry. Settings → Usage, the work-status popover, the mobile popover, and tray sync all iterate this list dynamically, so one entry covers every surfacepackages/vscodeextension hostquotaProviders.ts(DeepinfraPayloadtype,fetchDeepinfraQuota, dispatcher case,listConfiguredQuotaProvidersdetection)packages/web/server/lib/quota/DOCUMENTATION.mddeepinfraalias read from~/.local/share/opencode/auth.json; no routes, persisted settings, or other provider behavior changedElectron, hosted mobile, and Capacitor consume the web server runtime and the shared UI list, so they need no separate change.
/api/quota/:providerIdalready dispatches by id, so the new provider flows through the existing dispatcher.Repository guidance
AGENTS.mdruntime boundaries / cross-runtime parityAGENTS.mdcorrectness invariants (authoritative state, explicit failure)ok/configured/errorpreserved viabuildResult; 401/403 maps to session-expired; timeout and parse failures are classified explicitlyopenchamber-change-disciplineskillQUOTA_PROVIDERSconsumer (all dynamic iteration, no per-provider switch); focused tests plus per-package type-check and lint;bun run dead-codereports the same pre-existingfetch*Quotapattern, no new file/type findingquota/DOCUMENTATION.md"Add a new provider" checklistproviderId/providerName/aliases/isConfigured/fetchQuota, sharedutilshelpers, registry entry, docs row, testsdeepseek.jsand the VS CodefetchDeepseekQuota; window naming reuses the localizedcredits_balancelabelsync-state-invariantswas not loaded: no session sync, reducers, or polling were touched.Validation
bunx vitest run lib/quotafrompackages/webbun run --cwd packages/web testbun run --cwd packages/ui testbun run --cwd packages/vscode testbun run --cwd packages/ui type-check,--cwd packages/web type-check,--cwd packages/vscode type-checkbun run --cwd packages/ui lint,--cwd packages/vscode lintbun run dead-codefetchDeepinfraQuotalisted under unused exports, the same pre-existing pattern as every other named quota fetcher (Claude, OpenAI, DeepSeek, …). No file-level or type-level finding for the new modulebunx oxlinton changed pathsno-module-mockingfinding in the new web test, identical to all sibling provider tests (deepseek.test.js,codex.test.js, …); the newdeepinfra.jsis cleanauth.json;GET /api/quota/deepinfrareturnedcredits_balance$50.75, and Settings → Usage rendered the DeepInfra row with a Credits Balance $50.75 cardNot verified: full
bun run build(onlypackages/web buildwas used to render the screenshots); rootbun run testchain (per-package suites used instead); packaged Electron/mobile runtime (static analysis only — both consume the web runtime and shared UI list, so risk is low).Visual evidence
Settings → Usage. The "before" is the installed 1.23.1 UI (no DeepInfra); the "after" is this branch built on current
main(DeepInfra selected, live balance). Currentmainalready removed the unrelated CrofAI row, which is not part of this PR.Light theme only; no colors or styling changed.
Risks and failure behavior
Session expired — please re-authenticate with DeepInfra; the provider staysconfigured: trueso the UI shows the error instead of clearing state.Request timed out,Invalid response from provider, or the request message withok: false; the store keeps the failure signal, so a failure never looks like authoritative empty data.0renders as$0.00(valid); a positivestripe_balance(money owed) renders as-$X.XX; a missing or non-numeric balance yieldsNo quota data in response(ok: false,configured: true). All three are tested.auth.jsonstore and is never logged or persisted by this change; no secrets appear in tests or docs.deepinfra); no existing routes, settings, or provider behavior changed. Rollback is a single-commit revert.quota/DOCUMENTATION.md.