How Model Runtime Capabilities Determine Provider Listing in Pi Web

Pi Web builds its provider lists from the real-time capabilities reported by the ModelRuntime supplied by the @earendil-works/pi-coding-agent SDK, using a three-layer pipeline that queries authentication methods, credential status, and model counts.

Pi Web's Models panel doesn't rely on hard-coded provider definitions. Instead, it dynamically generates API-key and OAuth provider listings based on what the underlying runtime actually supports. This capability-driven architecture automatically adapts to SDK updates and user-specific configuration stored in auth.json or models.json.

The Three-Layer Provider Listing Pipeline

Layer 1: Collect Raw Provider Data from ModelRuntime

The function collectProviderListingInputs in lib/provider-listing-runtime.ts queries the runtime for complete provider metadata:

  • All available providers via modelRuntime.getProviders()
  • Each provider's authentication configuration (auth.apiKey?.login, auth.oauth)
  • Current authentication status via modelRuntime.getProviderAuthStatus(id)
  • Stored credentials via modelRuntime.listCredentials()
  • Model counts via modelRuntime.getModels()
// lib/provider-listing-runtime.ts
const models = modelRuntime.getModels();
const credentialTypes = new Map<string, ProviderCredentialType>();
// …populate credentialTypes from modelRuntime.listCredentials()
return modelRuntime.getProviders().map(provider => ({
  id: provider.id,
  name: provider.name,
  hasApiKeyLogin: Boolean(provider.auth.apiKey?.login),
  hasOAuth: Boolean(provider.auth.oauth),
  oauthName: provider.auth.oauth?.name,
  status: modelRuntime.getProviderAuthStatus(provider.id),
  credentialType: credentialTypes.get(provider.id),
  modelCount: models.filter(m => m.provider === provider.id).length,
}));

Layer 2: Deduplicate Provider Entries

The helper dedupeById in lib/provider-listing.ts removes duplicate entries that appear when a provider supports multiple authentication methods.

Layer 3: Build Distinct API-Key and OAuth Listings

buildApiKeyProviderList filters for API-key providers in lib/provider-listing.ts:

  • Excludes providers without hasApiKeyLogin
  • Skips custom models.json entries (identified via CUSTOM_PROVIDER_SOURCES)
  • Marks a provider as configured only when credentials exist and the credential type is not OAuth
// lib/provider-listing.ts
export function buildApiKeyProviderList(providers) {
  const result = [];
  for (const p of dedupeById(providers)) {
    if (!p.hasApiKeyLogin) continue;
    if (p.status.source && CUSTOM_PROVIDER_SOURCES.has(p.status.source)) continue;
    const configured = p.status.configured && p.credentialType !== "oauth";
    result.push({
      id: p.id,
      displayName: p.name,
      configured,
      ...(configured && p.status.source ? { source: p.status.source } : {}),
      modelCount: p.modelCount,
      supportsOAuth: p.hasOAuth,
    });
  }
  return result;
}

buildOAuthProviderList handles OAuth providers:

  • Includes only providers with hasOAuth enabled
  • Records login status via credentialType === "oauth"
  • Uses display name fallbacks: OAUTH_DISPLAY_NAMES[p.id], p.oauthName, or p.name
// lib/provider-listing.ts
export function buildOAuthProviderList(providers) {
  const result = [];
  for (const p of dedupeById(providers)) {
    if (!p.hasOAuth) continue;
    result.push({
      id: p.id,
      name: OAUTH_DISPLAY_NAMES[p.id] ?? p.oauthName ?? p.name,
      usesCallbackServer: false,
      loggedIn: p.credentialType === "oauth",
      supportsApiKey: p.hasApiKeyLogin,
    });
  }
  return result;
}

Practical Implementation Example

import { collectProviderListingInputs } from "@/lib/provider-listing-runtime";
import {
  buildApiKeyProviderList,
  buildOAuthProviderList,
} from "@/lib/provider-listing";

/** Example: retrieve both provider lists for the Models panel */
async function getProviderLists(modelRuntime) {
  const inputs = await collectProviderListingInputs(modelRuntime);
  const apiKeyList = buildApiKeyProviderList(inputs);
  const oauthList = buildOAuthProviderList(inputs);
  return { apiKeyList, oauthList };
}

/* Usage inside an API route (app/api/models-config/catalog/route.ts) */
export async function GET() {
  const runtime = await getModelRuntime(); // provided by pi-coding-agent
  const { apiKeyList, oauthList } = await getProviderLists(runtime);
  return Response.json({ apiKeyList, oauthList });
}

Key Source Files in agegr/pi-web

File Purpose
lib/provider-listing.ts Pure helpers that dedupe providers and build API-key & OAuth listings.
lib/provider-listing-runtime.ts Adapter that transforms ModelRuntime into helper-compatible input shapes.
app/api/models-config/catalog/route.ts API entry point serving provider data to the UI.

Summary

  • Runtime-driven discovery: Pi Web queries ModelRuntime methods (getProviders(), getProviderAuthStatus(), listCredentials(), getModels()) rather than using static provider definitions.
  • Dual listing generation: Separate functions produce API-key and OAuth provider lists with distinct filtering and metadata logic.
  • Automatic adaptation: The system responds to SDK changes (new providers, auth method changes) and user configuration without code modifications.
  • Credential-aware status: A provider's "configured" state depends on actual stored credentials and their type, preventing false positives for OAuth-authenticated providers.

Frequently Asked Questions

How does Pi Web handle providers that support both API-key and OAuth authentication?

Pi Web treats dual-auth providers as separate entries in each list. The dedupeById helper prevents duplicates within a single list, but buildApiKeyProviderList includes supportsOAuth: true to indicate hybrid capability, while buildOAuthProviderList includes supportsApiKey: true. This allows the UI to show appropriate configuration options for each authentication method.

Why does the configured status require credentialType !== "oauth" for API-key providers?

This check prevents confusion when a user authenticates via OAuth. Without it, the API-key provider would appear configured despite having no API key stored. The credentialType check ensures the "configured" badge accurately reflects API-key readiness, not general authentication status.

What happens when a provider switches from OAuth-only to dual-auth support?

The Pi Web UI automatically adapts. The modelRuntime.getProviders() call returns updated auth configuration, collectProviderListingInputs sets both hasApiKeyLogin and hasOAuth flags, and both listing functions include the provider in their respective outputs. No manual provider list updates are required.

Where does Pi Web store provider credentials and how are they accessed?

Credentials live in auth.json (API keys) and are retrieved via modelRuntime.listCredentials(). OAuth tokens are similarly stored and tracked. The modelRuntime.getProviderAuthStatus(id) method abstracts the storage details, returning a standardized status object that the listing pipeline consumes.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →