> ## Documentation Index
> Fetch the complete documentation index at: https://gateway.consus.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Pi

> Use the Pi coding agent with ITAR and FedRAMP High data

<Warning>
  Your `models.json` should list only the models authorized for **your data's classification**. Choose the compliance level your data classification and authorization require, list models at that level, and do not mix levels within a single `models.json`. Step 4 gives two examples to base yours on: an **ITAR** configuration and an **All models** configuration.

  The gateway enforces the compliance boundary for the models it serves, but it does **not** control client-side behavior: local tool execution, file access, outbound destinations, and so on. Client-side behavior is the customer's responsibility.
</Warning>

<video width="100%" style={{ aspectRatio: "16/9" }} controls>
  <source src="https://mintcdn.com/consusindustries/yUpTXeaaOPggocW0/assets/pi_coding_agent.mp4?fit=max&auto=format&n=yUpTXeaaOPggocW0&q=85&s=2e218460436ca00cd5267a8eaac8e77d" type="video/mp4" data-path="assets/pi_coding_agent.mp4" />
</video>

## Before you start

You need a **Consus API key** from your Consus admin. Installing [Pi](https://pi.dev) and creating its config are covered in the steps below.

## Step 1: Install Pi

Install with the official script:

```bash theme={null}
curl -fsSL https://pi.dev/install.sh | sh
```

Or with npm:

```bash theme={null}
npm install -g --ignore-scripts @earendil-works/pi-coding-agent
```

Confirm it's on your PATH:

```bash theme={null}
pi --version
```

## Step 2: Bootstrap the config directory

Pi reads custom providers from `~/.pi/agent/models.json`. Launch Pi once to create the `~/.pi/agent/` directory, then exit with `Ctrl+C`:

```bash theme={null}
pi
```

If the directory still isn't there, create it yourself:

```bash theme={null}
mkdir -p ~/.pi/agent
```

## Step 3: Set your Consus API key

Export your key in your shell profile (`~/.zshrc` or `~/.bashrc`) so Pi can read it at launch:

```bash theme={null}
export CONSUS_API_KEY="<CONSUS_API_KEY>"
```

Replace `<CONSUS_API_KEY>` with your actual key, then `source ~/.zshrc` (or `source ~/.bashrc`).

<Warning>
  Keep your `CONSUS_API_KEY` out of source control and out of `models.json`. The config references it as `$CONSUS_API_KEY`, and Pi resolves it from the environment at request time.
</Warning>

## Step 4: Create `~/.pi/agent/models.json`

Create the file:

```bash theme={null}
touch ~/.pi/agent/models.json
```

Then populate it with the models authorized for your data classification. The two examples below use the identical provider block and differ only in the `models` array: one scoped to ITAR / export-controlled data, one listing a broad set of models across compliance levels. Use whichever matches your authorization, or adapt the model list to your classification. Do not mix levels within a single file.

### Example: ITAR

Use this when handling ITAR / export-controlled data. It exposes **Claude Opus 4.8**, **Claude Sonnet 4.5**, and **GPT-5.4**, all with ITAR authorization on AWS GovCloud Bedrock. Opus 4.8 is authorized for ITAR only, so it uses the standalone `:itar` ID.

<Accordion title="models.json (ITAR)">
  ```json theme={null}
  {
    "providers": {
      "consus": {
        "baseUrl": "https://api.consus.io/v1",
        "api": "openai-completions",
        "apiKey": "consus",
        "headers": {
          "x-api-key": "$CONSUS_API_KEY"
        },
        "compat": {
          "supportsDeveloperRole": false,
          "supportsReasoningEffort": true,
          "thinkingFormat": "reasoning_effort",
          "supportsStore": false,
          "supportsStrictMode": false,
          "maxTokensField": "max_tokens"
        },
        "models": [
          {
            "id": "claude-opus-4-8:itar",
            "name": "Claude Opus 4.8 (ITAR)",
            "reasoning": true,
            "thinkingLevelMap": {
              "off": "none",
              "minimal": "low",
              "low": "low",
              "medium": "medium",
              "high": "high",
              "xhigh": "xhigh"
            },
            "input": [
              "text",
              "image"
            ],
            "contextWindow": 1000000,
            "maxTokens": 8192
          },
          {
            "id": "claude-sonnet-4-5:itar",
            "name": "Claude Sonnet 4.5 (ITAR)",
            "reasoning": true,
            "thinkingLevelMap": {
              "off": "none",
              "minimal": "low",
              "low": "low",
              "medium": "medium",
              "high": "high",
              "xhigh": "xhigh"
            },
            "input": [
              "text",
              "image"
            ],
            "contextWindow": 200000,
            "maxTokens": 8192
          },
          {
            "id": "gpt-5.4:itar",
            "name": "GPT 5.4 ITAR",
            "reasoning": true,
            "thinkingLevelMap": {
              "off": "none",
              "minimal": "low",
              "low": "low",
              "medium": "medium",
              "high": "high",
              "xhigh": "xhigh"
            },
            "input": [
              "text",
              "image"
            ],
            "contextWindow": 400000,
            "maxTokens": 8192
          }
        ]
      }
    }
  }
  ```
</Accordion>

### Example: All models

Lists the models people actually reach for, spanning several compliance levels so you can pick or switch from Pi's picker with `/model` (or `Ctrl+L`) mid-session. Each `id` carries its own level; trim this list, or swap the levels (`:il5+itar`, `:itar`, `:il2`, `:il4`, `:fedramp-high`, and so on), to match your data classification and authorization.

<Accordion title="models.json (all models)">
  ```json theme={null}
  {
    "providers": {
      "consus": {
        "baseUrl": "https://api.consus.io/v1",
        "api": "openai-completions",
        "apiKey": "consus",
        "headers": {
          "x-api-key": "$CONSUS_API_KEY"
        },
        "compat": {
          "supportsDeveloperRole": false,
          "supportsReasoningEffort": true,
          "thinkingFormat": "reasoning_effort",
          "supportsStore": false,
          "supportsStrictMode": false,
          "maxTokensField": "max_tokens"
        },
        "models": [
          {
            "id": "claude-sonnet-4-5:il5+itar",
            "name": "Claude Sonnet 4.5 (IL5 + ITAR)",
            "reasoning": true,
            "thinkingLevelMap": {
              "off": "none",
              "minimal": "low",
              "low": "low",
              "medium": "medium",
              "high": "high",
              "xhigh": "xhigh"
            },
            "input": [
              "text",
              "image"
            ],
            "contextWindow": 200000,
            "maxTokens": 8192
          },
          {
            "id": "claude-sonnet-4-5:itar",
            "name": "Claude Sonnet 4.5 (ITAR)",
            "reasoning": true,
            "thinkingLevelMap": {
              "off": "none",
              "minimal": "low",
              "low": "low",
              "medium": "medium",
              "high": "high",
              "xhigh": "xhigh"
            },
            "input": [
              "text",
              "image"
            ],
            "contextWindow": 200000,
            "maxTokens": 8192
          },
          {
            "id": "claude-opus-4-7:il2",
            "name": "Claude Opus 4.7 (IL2)",
            "reasoning": true,
            "thinkingLevelMap": {
              "off": "none",
              "minimal": "low",
              "low": "low",
              "medium": "medium",
              "high": "high",
              "xhigh": "xhigh"
            },
            "input": [
              "text",
              "image"
            ],
            "contextWindow": 1000000,
            "maxTokens": 8192
          },
          {
            "id": "claude-opus-4-6:il2",
            "name": "Claude Opus 4.6 (IL2)",
            "reasoning": true,
            "thinkingLevelMap": {
              "off": "none",
              "minimal": "low",
              "low": "low",
              "medium": "medium",
              "high": "high",
              "xhigh": "xhigh"
            },
            "input": [
              "text",
              "image"
            ],
            "contextWindow": 1000000,
            "maxTokens": 8192
          },
          {
            "id": "claude-sonnet-4-6:il2",
            "name": "Claude Sonnet 4.6 (IL2)",
            "reasoning": true,
            "thinkingLevelMap": {
              "off": "none",
              "minimal": "low",
              "low": "low",
              "medium": "medium",
              "high": "high",
              "xhigh": "xhigh"
            },
            "input": [
              "text",
              "image"
            ],
            "contextWindow": 1000000,
            "maxTokens": 8192
          },
          {
            "id": "claude-haiku-4-5:il2",
            "name": "Claude Haiku 4.5 (IL2)",
            "reasoning": true,
            "thinkingLevelMap": {
              "off": "none",
              "minimal": "low",
              "low": "low",
              "medium": "medium",
              "high": "high",
              "xhigh": "xhigh"
            },
            "input": [
              "text",
              "image"
            ],
            "contextWindow": 200000,
            "maxTokens": 8192
          },
          {
            "id": "gemini-3-1-pro:fedramp-high",
            "name": "Gemini 3.1 Pro (FedRAMP High)",
            "input": [
              "text",
              "image"
            ],
            "contextWindow": 1048576,
            "maxTokens": 8192
          },
          {
            "id": "gpt-5.4:il5+itar",
            "name": "GPT 5.4 (IL5 + ITAR)",
            "reasoning": true,
            "thinkingLevelMap": {
              "off": "none",
              "minimal": "low",
              "low": "low",
              "medium": "medium",
              "high": "high",
              "xhigh": "xhigh"
            },
            "input": [
              "text",
              "image"
            ],
            "contextWindow": 400000,
            "maxTokens": 8192
          },
          {
            "id": "gpt-5.4:itar",
            "name": "GPT 5.4 ITAR",
            "reasoning": true,
            "thinkingLevelMap": {
              "off": "none",
              "minimal": "low",
              "low": "low",
              "medium": "medium",
              "high": "high",
              "xhigh": "xhigh"
            },
            "input": [
              "text",
              "image"
            ],
            "contextWindow": 400000,
            "maxTokens": 8192
          },
          {
            "id": "gpt-oss-120b:il5+itar",
            "name": "GPT OSS 120B (IL5 + ITAR)",
            "reasoning": true,
            "thinkingLevelMap": {
              "off": "none",
              "minimal": "low",
              "low": "low",
              "medium": "medium",
              "high": "high",
              "xhigh": "xhigh"
            },
            "input": [
              "text"
            ],
            "contextWindow": 128000,
            "maxTokens": 8192
          },
          {
            "id": "gpt-oss-120b:itar",
            "name": "GPT OSS 120B (ITAR)",
            "reasoning": true,
            "thinkingLevelMap": {
              "off": "none",
              "minimal": "low",
              "low": "low",
              "medium": "medium",
              "high": "high",
              "xhigh": "xhigh"
            },
            "input": [
              "text"
            ],
            "contextWindow": 128000,
            "maxTokens": 8192
          },
          {
            "id": "gemini-3-5-flash:il4",
            "name": "Gemini 3.5 Flash",
            "reasoning": true,
            "thinkingLevelMap": {
              "off": "none",
              "minimal": "low",
              "low": "low",
              "medium": "medium",
              "high": "high",
              "xhigh": "xhigh"
            },
            "input": [
              "text",
              "image"
            ],
            "contextWindow": 1048576,
            "maxTokens": 8192
          }
        ]
      }
    }
  }
  ```
</Accordion>

<Note>
  This is a starting point. Trim it to the models you actually use, or add any other authorized model with the same shape. Run `curl -H "x-api-key: $CONSUS_API_KEY" https://api.consus.io/v1/models` to list every composite ID available to your key. Every `id` must carry a compliance level (e.g. `:fedramp-high`, `:il5`, `:itar`); a bare model name is rejected with a `400`.
</Note>

## Step 5: Start your session and pick a model

```bash theme={null}
cd /path/to/your/project
pi
```

Open the model picker with `/model` (or `Ctrl+L`) and select one of the Consus models you defined. Pi reloads `models.json` every time you open the picker, so you can edit the file and re-open `/model` without restarting.

## Step 6: Verify

Ask Pi to do something real:

```
> create a file hello.py with a fibonacci function and run it
```

The file should appear on disk and the run output should be the actual numbers, not a hallucinated transcript. On the gateway side, the response header `x-consus-served-model` confirms which model served each successful request.

## What gets served

Every request from Pi (coding turns, reasoning, tool use) is served by the model you select in the picker. With the **ITAR** example that's Claude Sonnet 4.5 or GPT-5.4 with ITAR authorization on AWS GovCloud Bedrock; with the **All models** example it's whichever model you pick, at the compliance level set in its ID. The gateway routes each request to whichever government-authorized provider hosts that model at the requested compliance level (AWS GovCloud Bedrock, GCP Assured Workloads Vertex AI, Azure Government OpenAI); the compliance boundary is enforced at the gateway architecture level.

Pi talks to the gateway over the OpenAI-compatible Chat Completions API. For the full request/response surface (multimodal input, streaming behavior, tool-use shape), see [`POST /v1/chat/completions`](/endpoints/chat-completions).

## What each setting does

`baseUrl`
: Routes Pi's requests to the gateway's OpenAI-compatible endpoint (`https://api.consus.io/v1`).

`api`
: `openai-completions` tells Pi to use the Chat Completions wire format, which the gateway exposes for every provider (Anthropic, OpenAI, Google).

`apiKey`
: A non-empty placeholder (`"consus"`). Consus authenticates with the `x-api-key` header, not `Authorization: Bearer`, so the real key is supplied via `headers` below. Do **not** set `authHeader: true`; a `Bearer` token is rejected with a `403`.

`headers."x-api-key"`
: Sends your Consus key on every request. `$CONSUS_API_KEY` is resolved from the environment (Step 3).

`compat`
: Tunes the wire format for the gateway. `supportsStore: false` (the gateway is zero-persistence), `maxTokensField: "max_tokens"` (not `max_completion_tokens`), `supportsDeveloperRole: false` (use the `system` role), `supportsStrictMode: false`, and `thinkingFormat: "reasoning_effort"` so reasoning is expressed as a `reasoning_effort` value.

`models[].id`
: The composite model ID Pi sends to the gateway. Must include a `:level` suffix.

`models[].reasoning` / `thinkingLevelMap`
: `reasoning: true` marks a model as supporting extended thinking. The gateway accepts only `none`, `low`, `medium`, `high`, and `xhigh` for `reasoning_effort` (any other token, including `minimal`, is a `400`), so every reasoning model uses the same `thinkingLevelMap` to translate Pi's levels onto that set: `off` maps to `none` (turns reasoning off where the provider allows it), `minimal` maps to `low`, and the rest map to themselves. `xhigh` is honored only by models that advertise it (GPT-5.4 here); on the others the gateway clamps it down to that model's maximum tier rather than erroring, so the one shared map is correct everywhere. Non-reasoning models (for example Gemini 3.1 Pro) omit both fields.

`models[].contextWindow` / `maxTokens`
: The model's input context window and the maximum output tokens Pi requests per turn.

## Troubleshooting

**`401 Unauthorized`**: `x-api-key` is missing or wrong. Verify `echo $CONSUS_API_KEY` returns your key in the same shell you launched `pi` from, and that the `headers."x-api-key"` line in `models.json` is present.

**`403 Forbidden`**: A `Bearer` token reached the gateway. Make sure `authHeader` is not set to `true` in your provider block; Consus authenticates only via `x-api-key`.

**`400 ... compliance level required`**: A model `id` is missing its `:level` suffix (e.g. `claude-opus-4-8` instead of `claude-opus-4-8:fedramp-high`). Add the level your authorization requires.

**`400 invalid_request_error` mentioning `reasoning_effort`**: A `thinkingLevelMap` value is outside the accepted set. Use only `none`, `low`, `medium`, `high`, or `xhigh`; anything else (such as `minimal`) is rejected. `xhigh` is fine on any model, since the gateway clamps it down to a model's maximum tier.

**`400` / thinking-budget errors at high effort**: A model's thinking budget can exceed `maxTokens`, which some providers reject. Raise `maxTokens` for that model or lower the reasoning effort.

**Model not appearing in `/model`**: `models.json` didn't parse. Validate it with `python3 -m json.tool ~/.pi/agent/models.json`, fix any error, and re-open the picker (Pi reloads the file each time).
