# pi-cliproxy-provider

通过 [CLIProxyAPI](https://github.com/router-for-me/CLIProxyAPI) 代理在 Pi 编码助手中访问 Claude、Gemini、GPT、Grok、Kimi 等多家模型。

## 前置条件

1. 已安装并运行 [CLIProxyAPI](https://github.com/router-for-me/CLIProxyAPI)（默认端口 `8317`）
2. CLIProxyAPI 的 `config.yaml` 中已配置至少一组 `api-keys` 及对应的模型凭证
3. Pi 编码助手 v1.0.0+

## 安装

```bash
pi install npm:@rotart/pi-cliproxy-provider
```

## 环境变量

| 变量 | 必填 | 默认 | 说明 |
|------|------|------|------|
| `CLIPROXY_API_KEY` | 是 | — | CLIProxyAPI 的 `api-keys` 中配置的密钥 |
| `CLIPROXY_BASE_URL` | 是 | — | CLIProxyAPI 地址（建议含 `/v1`），如 `http://localhost:8317/v1` |
| `CLIPROXY_BOOT_TIMEOUT_MS` | 否 | `5000` | 扩展启动时预拉模型超时（毫秒） |
| `CLIPROXY_RETRY_TIMEOUT_MS` | 否 | `5000` | `session_start` 自动补拉超时（毫秒） |
| `CLIPROXY_RELOAD_TIMEOUT_MS` | 否 | `30000` | `/cliproxy-reload` 手动刷新超时（毫秒） |

设置方式（以 PowerShell 为例）：

```powershell
$env:CLIPROXY_API_KEY = "your-api-key-1"
$env:CLIPROXY_BASE_URL = "http://localhost:8317/v1"
```

**BASE_URL 说明**：会自动 trim、去掉尾部 `/`，并避免拼出重复的 `/models`。扩展**不会**自动补上缺失的 `/v1`，请按代理实际挂载路径填写。

## 使用

1. 设置必填环境变量
2. 启动 CLIProxyAPI
3. 启动 Pi
4. 在 Pi 中执行 `/model`，选择 `CLIProxyAPI` Provider 下的模型
5. 若代理晚于 Pi 启动：通常会在会话开始时自动再试一次；仍失败则执行 `/cliproxy-reload`
6. 代理侧增删模型后，优先用 `/cliproxy-reload` 刷新（不必全局 `/reload`）

## 模型发现时序（1.1）

```text
扩展加载（async factory）
  └─ boot 预拉（默认 5s）
       ├─ 成功且非空 → 注册真实模型列表
       └─ 失败或 0 个模型 → 注册空壳（models: []）

会话开始 session_start
  ├─ 已有非空列表 → 仅通知一次「已加载 N 个模型」（若尚未展示）
  └─ 仍为空且本生命周期未补拉过 → 再试一次（默认 5s）

手动 /cliproxy-reload（默认 30s）
  ├─ 成功且非空 → 全量替换模型列表
  └─ 失败或 0 个模型 → 保留已有非空列表，并 warning 提示
```

并发的 boot / 补拉 / 手动刷新会合并为**同一次**进行中的请求。

## 模型能力发现

扩展会从 CLIProxyAPI 拉取你在 `config.yaml` 中配置的模型别名。

1. 若条目带有正数 `context_window` / `contextWindow` 或 `max_tokens` / `maxTokens` / `max_output_tokens`，优先使用这些值  
2. 其余字段由本地启发式推断  

| 模型关键词 | 推理 | 图像（默认） | 上下文窗口 | 最大输出 |
|-----------|------|-------------|-----------|---------|
| Claude 系列 | ✅ | ✅ | 200K | 8192 |
| GPT-5 系列 | ✅ | ✅ | 200K | 16384 |
| GPT-4o / GPT-4.1 | ✅ | ✅ | 200K | 16384 |
| Gemini 系列 | ✅ | ✅ | 1M | 8192 |
| 含 vision 的 ID | 视系列 | ✅ | 视系列 | 视系列 |
| Grok 3 系列 | ✅ | ✅ | 131K | 131072 |
| Grok 4 系列 | ✅ | ✅ | 1M / 500K | 131072 |
| grok-imagine-* | ❌ | ❌ | 1M | 131072 |
| Kimi 系列 | ✅ | ❌ | 128K | 16384 |
| GPT-4（非 4o/4.1） | ✅ | ❌ | 200K | 16384 |
| o 系列 | ✅ | ❌ | 200K | 32768 |
| 其他 | ❌ | ❌ | 128K | 4096 |

**说明**：

- 成本字段默认全为 0（不猜测定价）
- 不附带 `thinkingLevelMap`；需要精细思考档位时用 `modelOverrides`
- 白名单外的多模态模型、或要把某模型改回仅文本，请用 `modelOverrides`

## 高级配置：modelOverrides

在 `~/.pi/agent/models.json` 中使用 `modelOverrides`：

```json
{
  "providers": {
    "cliproxy": {
      "modelOverrides": {
        "claude-sonnet-latest": {
          "input": ["text", "image"],
          "contextWindow": 200000,
          "maxTokens": 16384,
          "cost": {
            "input": 3.0,
            "output": 15.0,
            "cacheRead": 0.3,
            "cacheWrite": 3.75
          }
        },
        "kimi-k2": {
          "input": ["text", "image"],
          "contextWindow": 262144,
          "maxTokens": 16384
        },
        "some-text-only-claude-alias": {
          "input": ["text"]
        }
      }
    }
  }
}
```

支持的覆盖字段：`name`、`reasoning`、`thinkingLevelMap`、`input`、`cost`、`contextWindow`、`maxTokens`、`headers`、`compat`。

## 开发

```bash
npm install
npm test
```

## 故障排查

| 问题 | 可能原因 | 解决 |
|------|---------|------|
| Provider 不可见 | 环境变量未设置 | 检查 `CLIPROXY_API_KEY` 和 `CLIPROXY_BASE_URL` |
| 模型列表为空（0 个模型） | CLIProxyAPI 未启动、不可达，或尚未配置模型 | 启动/配置代理后执行 `/cliproxy-reload` |
| 启动稍慢（约数秒） | boot 预拉在等代理 | 正常；可用 `CLIPROXY_BOOT_TIMEOUT_MS` 调小/调大 |
| 刷新失败但旧模型还在 | 失败或空列表保护 | 符合 1.1 设计；修好代理后再 `/cliproxy-reload` |
| 上下文被截断 | 启发式窗口不准且代理未返回 context 字段 | 通过 `modelOverrides` 设置 `contextWindow` |
| 推理/思考不可用 | 模型 ID 未匹配启发式 | `modelOverrides` 设置 `"reasoning": true` |
| 图像识别报错 / 非预期带图 | 不在白名单或白名单误匹配 | 用 `modelOverrides` 调整 `input` |
| 想重置扩展状态 | 自动补拉预算按扩展加载生命周期计算 | 使用 Pi 全局 `/reload` 重载扩展 |

## 许可

MIT
