# Pi

Pi, from Earendil Works. See the [Pi documentation](https://github.com/earendil-works/pi/tree/main/packages/coding-agent/docs) for its current Node and dependency requirements before installing, since those change independently of this page.

## Install

```
npm install -g --ignore-scripts @earendil-works/pi-coding-agent
pi --version
```

## Configure the provider

Pi reads custom providers from `~/.pi/agent/models.json`. One entry covers both hosted and self-hosted endpoints — only `baseUrl` changes.

`~/.pi/agent/models.json`

```
{
  "providers": {
    "ifm": {
      "baseUrl": "https://api.ifm.ai/v1",
      "api": "openai-completions",
      "apiKey": "$IFM_API_KEY",
      "compat": {
        "supportsDeveloperRole": false,
        "requiresReasoningContentOnAssistantMessages": true
      },
      "models": [
        {
          "id": "IFM/K2-Horizon-375B-A23B",
          "name": "K2 Horizon 375B",
          "reasoning": true,
          "input": ["text"],
          "contextWindow": 512000,
          "maxTokens": 32000
        }
      ]
    }
  }
}
```

| Field | Meaning |
| --- | --- |
| baseUrl | The OpenAI-compatible root, e.g. `https://api.ifm.ai/v1`. A self-hosted server substitutes its own address; nothing else in the profile changes. |
| api | Wire protocol. `openai-completions` for every IFM endpoint. Required — Pi refuses to load a provider without it. |
| apiKey | Read literally, or expanded from the environment when written as `$IFM_API_KEY`, so the key itself never goes in the file. |
| compat.supportsDeveloperRole | Must be `false`. IFM rejects the `developer` role that Pi otherwise sends to reasoning models. |
| compat.requiresReasoningContentOnAssistantMessages | Must be `true`. Makes Pi replay the reasoning field on every assistant message, including when the trace is empty. |
| models[].id | Model id. Must be an object with an `id`, not a bare string. |
| models[].reasoning | Must be `true`, or Pi treats the model as non-thinking and never carries a trace. |
| models[].contextWindow, maxTokens | Planning limits. Omit them and Pi silently assumes a 128K context and a 16.4K output cap. |

Self-hosting: change `baseUrl` to your own server address. `apiKey` must still resolve to a non-empty value; a server that does not check authentication accepts any placeholder.

Startup defaults live in a separate file, `~/.pi/agent/settings.json`, as `defaultProvider` and `defaultModel`:

`~/.pi/agent/settings.json`

```
{
  "defaultProvider": "ifm",
  "defaultModel": "IFM/K2-Horizon-375B-A23B"
}
```

## Model routing

A model reference is `<provider>/<model>`, split on the **first** slash — so `ifm/IFM/K2-Horizon-375B-A23B` resolves to provider `ifm` and model `IFM/K2-Horizon-375B-A23B`, whose own id contains a slash.

```
export IFM_API_KEY="sk-ifm-..."

# explicit provider and model
pi --provider ifm --model IFM/K2-Horizon-375B-A23B

# combined reference
pi --model ifm/IFM/K2-Horizon-375B-A23B

# falls back to the settings.json defaults
pi
```

## Reasoning pass-through

Preserve and resend the model's reasoning field in every assistant turn; otherwise Pi drops it and multi-turn requests can fail with HTTP 400.

See [Reasoning](https://docs.ifm.ai/docs/reasoning.md) and the [multi-turn guide](https://docs.ifm.ai/docs/multi-turn.md).

## Tool calling

Pi's file and shell tools go over the standard tool-calling schema — no IFM-specific adapter layer. The model selects a declared tool and produces its arguments; executing the call stays with the agent. See [Custom function calling](https://docs.ifm.ai/docs/custom-function-calling.md).

_Last updated: October 15, 2026_
