Example

Resolve a provider credential in precedence order

See the four credential sources in the order Pi consults them, and why a model can load from models.json yet stay unavailable.

Pi resolves a provider credential from four sources and uses the first one that yields a value: a runtime API key, a stored auth.json credential, an apiKey in models.json, then the provider’s environment variable. This is a configuration and decision-table walkthrough of the Chapter 3 tests; every key below is a placeholder, and it resolves no real credential.

The four sources, in order

Order Source Where it comes from
1 runtime key --api-key, or a runtime API call
2 stored credential <agent-dir>/auth.json
3 model key apiKey on the provider in <agent-dir>/models.json
4 environment the provider’s environment variable

The order surprises people: a key in models.json sits below a stored credential, not above it. It exists to give a custom or self-hosted model its key; it does not override a key you already logged in with.

A stored credential has this shape (placeholder values):

{ "anthropic": { "type": "api_key", "key": "stored" } }

A custom endpoint in models.json supplies its key the same way:

{
  "providers": {
    "mylocal": {
      "baseUrl": "http://localhost:11434/v1",
      "api": "openai-completions",
      "apiKey": "dummy",
      "models": [{ "id": "my-model" }]
    }
  }
}

Observed resolution

The canonical test resolves the same sources through Pi’s real ModelRuntime, then removes them one at a time. The asserted winner at each step:

Sources present Resolved credential
runtime + auth.json + models.json + environment runtime
auth.json + models.json + environment auth.json
models.json + environment models.json
environment only environment
none no credential

Loading is not availability

The second half of the test is the symptom worth remembering. A provider entry in models.json can parse and the model can exist (getModel returns it) while it is absent from the available list until the provider has usable authentication. Adding a dummy apiKey is enough to make it appear. That is why a correct models.json can still leave /model empty.

Mechanism and limitations

Documented: the exported ModelRuntime, its getAuth and getAvailable methods, and the auth.json / models.json file shapes. Observed: credentials.test.ts drives the real runtime over real files and asserts every row of the resolution table, including the availability difference. It also shows that the same script behaves identically whether the provider’s key is written as a literal, $PI_TEST_KEY, or !command — provider configuration is not agent behaviour.

The fragments here are illustrative: they show the file shapes and the precedence, not a run on your machine, and nothing here proves how a real model behaves. A provider extension may define its own resolution; the order above is the documented default chain.

Full source and test .

Understand this example

Copy this prompt into your AI tool. No code runs here.

Apply this example

Copy this prompt into your AI tool. No code runs here.