# OpenAI 相容端點

以任何 OpenAI 相容的 `/v1` 端點作為後端：如何新增、URL 與 key 存在哪裡、已測試過哪些。

## 任何 OpenAI 相容端點

這 11 家之外，`compat`（**Local/Custom**）並不是第 12 家廠商，而是讓這份清單不封閉的擴充口。指向任何 OpenAI 相容的 `/v1` base URL（可選填 key），它就成為可用的後端：Ollama、LM Studio、vLLM、LiteLLM、自架 gateway，或上週才推出 OpenAI 形狀 API 的新廠商。要接幾個就接幾個，每一個都是獨立命名的項目。

內建兩個本機端點（`configs/jsons/local_compat.json`）：**Ollama Local**（`http://localhost:11434/v1`）與 **Llama.cpp Local**（`http://localhost:8080/v1`）。`/model add` 會以 `GET /models` 平行探測兩者（預算 500 ms），有回應者列在供應商清單最上方，直接進入模型選擇，不會寫入 `config.json`。端點模型以 `<name>@<model>` 註冊，端點名稱轉小寫（`ollama@gemma3:4b`）；舊的 `compat[NAME]@<model>` 形式仍可接受，並在 `config.json` 載入或儲存時改寫為新形式。

新增模型時（TUI `/model` → `add`，或 `GET /v1/provider/:provider/models`）會即時取得模型清單；本機或自訂端點則取自該端點自己的 `GET /models`。repo 內不再存放靜態模型目錄。

## 新增自訂 OpenAI 相容端點

使用 **Local/Custom**（`compat`），指向任何接受 OpenAI Chat Completions schema 的端點。URL 慣例沿用 Zed：**輸入到 `/v1` 為止**（例如 `http://192.168.1.10:4000/v1`）；router 會自行接上 `/chat/completions`。內建的 Ollama 與 llama.cpp 連接埠不需另建條目。

### 儲存拆分（URL 與 key）

| 項目 | 位置 | API |
|---|---|---|
| URL | `~/.config/agenvoy/config.json` 的 `compats[]` — `{provider, url}`，provider 名稱轉大寫 | `config.UpsertCompat` / `config.GetCompatURL`（`internal/session/config`） |
| API key | OS keychain | `keychain.Set("COMPAT_<NAME>_API_KEY", value)` |

`GetCompatURL` 先查 `compats`，再查內建本機端點。沒有 `COMPAT_<NAME>_URL` 這個 keychain key——它在一次「TUI 寫入 config、runtime 卻讀 keychain 導致永遠回退到 localhost」的 bug 後被移除。

### 已驗證的 compat 目標

| 目標 | 可用 | 說明 |
|---|---|---|
| Ollama | 是 | 內建於 `http://localhost:11434/v1` |
| llama.cpp server | 是 | 內建於 `http://localhost:8080/v1` |
| LM Studio | 是 | |
| vLLM | 是 | tool use 需 `--enable-auto-tool-choice --tool-call-parser <name>` |
| LiteLLM proxy | 是 | 以 virtual key 當作 Bearer token |
| Groq / Together / DeepInfra / Fireworks | 是 | |
| Azure OpenAI | 否 | 需要 `api-key` header（非 `Bearer`）與 `?api-version=`——不支援 |
| opencode Go（`opencode.ai/zen/go/v1`） | 否 | 要求依對話變動的 `x-opencode-session` header，未帶會回 `400 MissingSessionID`——不支援 |

### 相容的判準

只有「request body 加上 `Authorization: Bearer <key>` 就是全部契約」的目標才算相容。compat 通道不夾帶任何廠商私有 header，因此要求私有 header 的端點一律不在範圍內，body 再像 OpenAI 也一樣。收錄範圍限三類：自營模型的供應商、相容 OpenAI API 的 NIM，以及 Cloudflare。

opencode Go 是這條界線的實例。自 [2026-09-03 公告](https://x.com/opencode/status/2095410501400289576)起，其 gateway 拒絕任何未帶 `x-opencode-session` 的請求；該 header 為廠商私有，且值必須隨對話變動，好讓請求落到持有該對話 cache 的節點。Chat Completions 本身無狀態——對話狀態在 `messages`，協定沒有 session 概念——要求 client 追蹤並輪替廠商 header，已使該端點脫離相容集合。為單一廠商在 compat 通道開自訂 header 的口子，等於對所有廠商都開，因此維持關閉。
