# Custom model providers LearnHarness can register several independently named custom providers using its existing Pi AI runtime. This is configuration wiring, not another agent runtime: Pi owns the model protocols, request conversion, streaming, tools, thinking and usage accounting. There is no additional dependency on Pi coding-agent. ## Configuration location Create `models.json` in the LearnHarness app-data directory (the directory containing `settings.json`; `LH_DATA_DIR` relocates it). LearnHarness does not read `~/.pi/agent/models.json`, install a model server, or edit this file for you. Custom-provider setup UI is not included yet. Once configured, models appear in the existing model selectors when their credential/header references resolve. The file uses Pi's `providers` / `models` structure. For example: ```json { "providers": { "my-local-server": { "baseUrl": "http://127.0.0.1:11434/v1", "api": "openai-completions", "apiKey": "unused-local-key", "compat": { "supportsDeveloperRole": false, "supportsReasoningEffort": false, "maxTokensField": "max_tokens" }, "models": [ { "id": "your-installed-model", "name": "My local model", "contextWindow": 8192, "maxTokens": 1024, "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } } ] }, "my-gateway": { "baseUrl": "https://your-gateway.example/v1", "api": "openai-completions", "apiKey": "$MY_GATEWAY_API_KEY", "headers": { "X-Tenant": "${MY_GATEWAY_TENANT}" }, "models": [ { "id": "your-gateway-model-id", "contextWindow": 32768, "maxTokens": 4096, "cost": { "input": 1, "output": 3, "cacheRead": 0, "cacheWrite": 0 } } ] } } } ``` **Replace all example IDs, limits, prices and compatibility flags with the actual server/model configuration.** These numbers are examples, not recommendations or automatically detected capabilities. Rates are USD per million tokens. Explicit zero rates are allowed; LearnHarness does not assume that custom gateways are free. Verify the endpoint's billing independently. Use the exact API base URL your server documents. LearnHarness does not append `/v1`, discover models, infer capabilities from names, or download models. Ollama, vLLM, LM Studio, 9router and other gateways work only to the extent that their configured endpoint implements the selected Pi protocol and compatibility flags. For a keyless OpenAI-compatible local server, supply an explicit nonempty dummy `apiKey` as above: the native Pi OpenAI client requires a key. Omitting `apiKey` leaves a provider unavailable rather than implying unauthenticated access. ## Supported fields Provider fields: `name`, `baseUrl`, `api`, `apiKey`, `headers`, `authHeader`, `compat`, and the explicit `models` array. Each model requires `id`, `contextWindow`, `maxTokens`, and `cost` with all four base rates. Unlike Pi coding-agent's defaults, LearnHarness never invents limits or prices. Optional Pi model fields are `name`, `api`, `baseUrl`, `reasoning`, `input`, `thinkingLevelMap`, `inputLimits`, `promptCache`, `samplingParams`, `samplingParamsByThinkingLevel`, `headers`, and `compat`. Cost tiers and Pi's exported compatibility schemas are supported. A model's `api` and `baseUrl` override its provider's values; model headers override provider headers. Names default to model IDs, `reasoning` defaults to false, and `input` defaults to `["text"]`. Declare image input only when the server/model supports it. Existing onboarding and vision-slot requirements are unchanged. The native chat transports available in the pinned Pi AI 1.4.0 dependency are: `openai-completions`, `openai-responses`, `anthropic-messages`, `google-generative-ai`, `azure-openai-responses`, `google-vertex`, `bedrock-converse-stream`, `mistral-conversations`, `openai-codex-responses`, and `pi-messages`. Protocol availability does **not** establish account access, OAuth support, cloud credentials, subscription permission, or vendor approval. This feature adds no login flows. In particular, an Anthropic Messages endpoint is not a Claude Agent SDK or Claude Code runtime. Use new provider IDs; built-in and subscription-route IDs cannot be replaced. Provider IDs must be nonempty and cannot contain `:` (the provider/model coordinate separator). Model IDs must be unique within a provider. Built-in provider overrides, `modelOverrides`, OAuth definitions, executable plugins, and automatic discovery are outside this phase. ## Credentials and trust API keys and header values accept literals, `$NAME`, and `${NAME}`. References use LearnHarness's existing key lookup: environment first, then the existing flat `auth.json` file, respecting the fresh-user environment-ignore setting. There is no auth migration or new secret file format. Prefer references over putting secrets into `models.json`; protect any file containing literal secrets. Unknown variable references keep the provider unavailable. All required header references, including model-specific ones, must resolve before a provider becomes available. An empty key does not configure a provider. Pi's `$$` and `$!` literal escapes are supported. `!command` credential/header values are rejected, never executed; resolved environment/file values are data, not commands. Optional `authHeader: true` adds `Authorization: Bearer ` in addition to the selected native transport's normal authentication behavior. Only trusted people should edit this file: its endpoints receive model prompts, attachments, tool results and configured credentials. Only HTTP(S) URLs are accepted, without embedded URL credentials or fragments. This is not an arbitrary-endpoint discovery or config-editing API and does not add a network sandbox, endpoint reachability checks, or vendor policy exemptions. ## Reloading and failures Changes take effect on the next catalog, settings-validation, or inference operation without restarting. Credential/header reference changes are also picked up through the existing key lookup. Every upstream model operation revalidates the same saved provider/model ID against the current Pi catalog, including later requests inside a long-running agent/tool loop. It uses the current endpoint, model metadata and resolved credentials. A removed model, invalid configuration or missing credential blocks the next operation, without falling back. An HTTP operation already in flight retains its own native Pi setup and is not implicitly cancelled by a file edit. Deleting the file means no custom providers. Invalid JSON, invalid metadata, reserved IDs, unsupported configuration, or an unreadable file disables the custom routes, **not** built-in providers. The file accepts Pi's line comments and trailing commas. `GET /api/models` includes an optional sanitized `customProvidersError` diagnostic; rendering it in setup/settings UI is deferred. Correcting the file restores valid providers on the next operation. Saved selections and thinking preferences are not erased when a provider/model is removed or becomes unavailable. New work using an unavailable selection fails rather than falling back to another model. Select another configured model to repair the slot. Custom-provider removal does not rewrite historical records; like existing API-key models, historical cost estimates use the current catalog and become unknown when the relevant model definition is unavailable.