# pi-ocgo mini —— 功能说明（OCGo Gateway 的 Pi 端适配壳）

> 分支：`mini` ｜ 对应网关仓库：`ocgo-gateway`（端口 8130）
>
> 一句话：**pi-opencodego 的极简网关配合版**。
> 上游 key 池、配额、用量、绑定、总控面板全部由独立网关 `ocgo-gateway` 负责，
> 本插件只做 Pi 宿主侧四件薄事：**指网关、注入身份、注入对话、薄命令**。

---

## 1. 这是什么

原本的 `pi-opencodego` 是一个"厚插件"：它自己维护多 key 池、做饭配额感知、
自己写用量统计、自己起 Web 面板——每个 Agent 宿主各来一套，重复且分裂。

`ocgo-gateway` 把这些能力**收拢为一个独立常驻网关**，所有 Agent（Pi / DSH / Hermes…）
共享同一个 key 池与总控面板。于是 Pi 端插件可以瘦成一层"适配壳"。

**mini 版就是这个壳。** 它不再拥有 key、不再算配额、不再记用量、不再起面板——
这些全部通过网关完成。插件只做宿主（Pi）侧做不到的、必须跑在 Pi 进程内的事。

---

## 2. 分工一览：网关 vs 插件

```
┌─────────────────────────────┐
│         Pi (宿主)           │
│  ┌───────────────────────┐  │
│  │  pi-ocgw (本插件)         │  │
│  │  · provider 指向网关   │  │      ┌──────────────────┐
│  │  · 注入 gateway-key    │  │      │  ocgo-gateway     │
│  │  · 注入对话 header     │──┼─────▶│  (常驻, 8130)      │
│  │  · /ocgo 薄命令        │  │  ▶1  │  · key 池/轮换     │──▶ OpenCode Go
│  │  · developer 兼容改写  │  │      │  · 配额/冷却        │    / Zen 上游
│  │  · 缓存前缀优化        │  │      │  · 对话级绑定       │
│  │  · 上下文交接提醒      │  │      │  · gateway-key 签发 │
│  └───────────────────────┘  │      │  · 用量统计         │
└─────────────────────────────┘      │  · 总控 Web 面板    │
                                     └──────────────────┘
     ① 插件用 gateway-key 调网关控制面 API，完成查看/绑定/切 key
```

| 能力 | 归谁 | 说明 |
|------|------|------|
| 多 key 池 / 轮换 / 冷却 | **网关** | 插件不再持有任何上游 sk- key |
| 配额感知 / 限流 | **网关** | 60s 节流打真实 /usage 接口 |
| 用量 / 费用统计 | **网关** | usage.ndjson + 面板三维度汇总 |
| 对话级绑定（bind） | **网关** | 插件只负责「告诉网关当前对话是谁」 |
| gateway-key 签发/吊销 | **网关** | 总控面板操作 |
| 总控 Web 面板 | **网关** | `http://127.0.0.1:8130/admin` |
| 指向网关 provider | **插件** | baseUrl → `127.0.0.1:8130/v1` |
| 身份注入（gateway-key） | **插件** | `Authorization: Bearer ocgo_...` |
| 对话标识注入 | **插件** | `x-ocgo-conversation: <会话ID>` |
| developer 兼容改写 | **插件** | 修 400（网关透传做不了，需宿主钩子） |
| 缓存前缀优化 | **插件** | 省 ~30 倍费（需宿主上下文钩子） |
| 状态/统计输出 | **插件呈现层 + 网关数据** | TUI 进度条/footer/推送 在宿主展示，数据走网关 API |
| /ocgw 薄命令 | **插件** | 全部转发网关 API / 本地配置 |

---

## 3. 插件保留的能力

### 3.1 指网关（provider）

注册 `pi-ocgw` provider，`baseUrl` 指向本机网关：

```
baseUrl  = http://127.0.0.1:8130/v1   （可配）
apiKey   = ocgo_xxx（占位，真正注入在请求头）
型号     = deepseek-v4-flash / -pro / glm-5.1 / kimi-k2.6 / qwen3.6-plus
```

### 3.2 注入身份（gateway-key）

每次请求自动加：

```
Authorization: Bearer ocgo_VoQq...（本插件的 gateway-key）
```

网关据此识别"这是 Pi 在请求"，绑定到 Pi 的默认上游或对话绑定。

### 3.3 注入对话标识

每次请求自动加：

```
x-ocgo-conversation: <当前 Pi 会话 ID>
```

网关据此做**对话级绑定**——你可以把某条对话钉到某个具体上游 K，
中途可换，不干扰其它会话。

### 3.4 薄命令（/ocgw）

全部通过网关控制面 API / 本地配置完成，插件端不落任何上游 key：

| 命令 | 层级 | 作用 | 去向 |
|------|------|------|------|
| `/ocgw setup <url> <key>` | **Agent 级** | 一次设置：网关地址 + gateway-key | 本地配置 |
| `/ocgw url <http://...>` | **Agent 级** | 只改网关地址 | 本地配置 |
| `/ocgw key <ocgo_...>` | **Agent 级** | 只改 gateway-key | 本地配置 |
| `/ocgw whoami` | **Agent 级** | 看生效地址/身份 | 本地+网关 |
| `/ocgw bind [<key名>]` | **对话级** | 当前会话绑定；缺省=绑当前 active key | 网关 `/admin/bind` |
| `/ocgw unbind` | **对话级** | 当前会话回默认 | 网关 `/admin/unbind` |
| `/ocgw default <key名>` | **Agent 级默认** | 设 Pi 默认上游 K（未绑定对话走它） | 网关 `/admin/set-default` |
| `/ocgw status` | 对话级 | 看 key 池 + 当前绑定 | `GET /admin/list` |
| `/ocgw quota [key名]` | 对话级 | 查配额（缺省=当前生效 key） | `GET /admin/quota` |
| `/ocgw cost [范围]` | 对话级 | 查用量/费用（today/3d/7d/all） | `GET /admin/stats` |
| `/ocgw add <名> <key>` | 网管 | 加上游 key | `POST /admin/key/add` |
| `/ocgw rm <名>` | 网管 | 删上游 key | `POST /admin/key/remove` |
| `/ocgw expire/revive/pause` | 网管 | key 状态管理 | 网关对应接口 |

> ①、②（网址+身份）是 **Agent 级、全局、管一次**；③（绑定哪个 K）是 **对话级、可变、归网关**。

---

## 4. 宿主侧保留能力（网关做不了，必须留插件里）

这两项**不是"可选项"**——已核实网关转发链路是纯透传，没有内置它们：

### 4.1 developer 兼容改写
某些上游模型拒绝 `role:"developer"` 直接 400。
插件在 `before_provider_request` 把 `developer → system`（幂等，有则改、无则不动）。

### 4.2 缓存前缀优化（DeepSeek 计费命门）
DeepSeek 按**字节级前缀**缓存提示，缓存命中 ≈ 1/30 全价。
插件做三件事，最大化命中率：
- 删消息里的 `reasoning_content` / thinking 块（每轮不同、撑爆上下文）
- 删系统提示词里每轮变化的时间戳
- 按名字确定性排序 tools schema（序列化顺序稳定）

> **上下文交接提醒（handoff）**：**保留（成熟功能，比 pi 压缩好）**。  
> pi 的 `/compact` 会用专门 system prompt 把历史序列化成 `<conversation>` 文本 → 前缀分叉 → 全价 miss（贵 ~30 倍）；
> handoff 在**正常对话里**让模型生成交接文档（前缀延续 → 缓存命中 ≈ 1/30 全价），换 K/新建对话的成本从 X 降到 S。  
> 双水位阈值（80% 提醒 / 95% 紧急）+ 交接文档落盘 `docs/handoff/` + 新建会话注入继续，命令 `/ocgw handoff` / `/ocgw resume`。

### 4.3 上下文交接（handoff）

完整流程（与现有实现一致）：

1. **双水位提醒**：上下文 ≥80% 主动提醒、≥95% 紧急提醒；rolling 配额 ≥90% 也会触发（建议换 K/交接）。
2. **执行**（`/ocgw handoff` 确认后自动执行）：
   - 在当前会话里让模型生成**交接文档**（模板：Goal / Constraints / Progress / Key Decisions / Next Steps / Critical Context / Files）
   - 注入 pi 跟踪的 read/modified 文件清单，篇幅 ≤30K 精炼
   - 落盘到项目 `docs/handoff/YYYY-MM-DD-HHmmss-handoff.md`
   - **新建会话**：把交接文档作为首条消息（前缀极小 S）→ 通知网关解绑旧会话、绑新 K（`/admin/unbind` + `/admin/bind`）
3. **恢复**（`/ocgw resume`）：列出 `docs/handoff/` 下所有交接文档 → 选择 → 注入新会话继续。

> 关键对比：pi 自带的 `/compact` 压缩后前缀分叉 → 历史全价 miss（≈X·u，贵 30 倍）；
> handoff 在正常对话里生成交接文档 → 前缀延续 → 缓存命中（≈X·h ≈ 1/30 全价）——**这是本插件比 pi 原生体验更好的核心卖点，必须保留**。

---

## 4.5 统计 / 配额输出（保留，数据走网关）

**这些输出不会消失**——只是数据源从插件本地换成网关 API，呈现层（TUI 进度条、footer 摘要、
定时推送、命令输出）完全保留：

```
★ k3: R0% · W62% · M31% →(09/17 04:45)

k3 总统计: 1,352 次 · 输入 7,561,547 · 输出 527,671 · 缓存命中 323,647,872 (97.72%)

⏱ 统计发送时间 22:10:18
```

| 输出 | 数据来源 |
|------|----------|
| TUI 左栏每个 key 的限额进度条（R/W/M + 重置时间） | 网关 `GET /admin/quota`（网关已 60s 节流缓存） |
| footer 摘要（激活 key 限额一行） | 同上 |
| 定时推送（限额 + 总统计 + 发送时间） | 网关 `GET /admin/quota` + `GET /admin/stats` |
| `/ocgw quota` / `/ocgw cost` / `/ocgw status` | 网关对应 API |

---

## 5. 删掉了什么（对比原版 pi-opencodego）

| 原版能力 | mini 版 | 原因 |
|----------|---------|------|
| 本地 key 池 / 轮换 / 冷却 | ❌ 删 | 归网关 |
| 直接打上游 /usage | ❌ 删 | 归网关 `/admin/quota` |
| 本地用量落盘 | ❌ 删 | 归网关 `usage.ndjson` |
| 自带 Web 配额面板 | ❌ 删 | 归网关 `/admin` 总控 |
| 429 自动轮换 | ❌ 删 | 网关按配额决策 |
| 上下文交接（handoff） | ✅ **保留** | 成熟功能，比 pi 压缩好（§4.3） |
| 上下文交接（handoff） | ✅ **保留** | 成熟功能，比 pi 压缩好（§4.3） |
| 会话粘合选 key | ❌ 删 | 网关按 bind 决策 |
| sessionAffinity / 多 key 配置 | ❌ 删 | 配置只剩网关地址 + gateway-key |
| **统计 / 配额 / 费用输出**（widget/footer/推送/命令） | ✅ **保留**（数据改走网关 API） | 呈现层不动，只换数据源（§4.5） |

**配置极简**──整个插件只存两样：

```jsonc
// ~/.pi/agent/ocgo-gateway.json
{
  "gatewayBase": "http://127.0.0.1:8130/v1",
  "gatewayKey": "ocgo_VoQq..."     // 网关面板签发的 Pi 身份
}
```

---

## 6. 使用流程

> 两个层级：**Agent 级**（Pi 全局：base URL + gateway-key，管一次）vs
> **对话级**（每会话：绑定到哪个上游 K，可随时改）。

```bash
# 1. 网关侧（一次）：
#    - 起网关  ocgo-gateway
#    - 面板 http://127.0.0.1:8130/admin 添加 K1/K2/K3 上游 key
#    - 面板给 Pi 签发 gateway-key（或 curl /admin/issue-key）

# 2. Agent 级：Pi 的"身份 + 连地址"，只设一次（写本地配置）
/ocgw setup http://127.0.0.1:8130/v1 ocgo_VoQq...   # 地址 + gateway-key 一次设好
/ocgw whoami                                        # 确认生效的地址/身份
/model pi-ocgw/deepseek-v4-flash

# 3. 对话级：每条对话选走哪条上游 K（写网关，可随时改）
/ocgw bind k2        # 本对话钉到 k2
/ocgw unbind         # 本对话回到 Pi 默认
/ocgw default k1     # 设 Pi 级默认（未绑定对话都走 k1）

# 4. 日常：
/ocgw status            # 看 key 池与绑定
/ocgw quota             # 看配额
/ocgw cost              # 看用量/费用
# 面板：http://127.0.0.1:8130/admin （总控：key 池/用量/绑定）

# 网关搬了地址 / 重签了 key，改 Agent 级：
/ocgw url http://新地址:8130/v1
/ocgw key ocgo_新key
```

> **为什么拆两层**：base URL 和 gateway-key 是整个 Pi 的身份，对话换绑只是切换
> 本条对话的额度通道——换 100 次对话也不需要重配身份。

---

## 7. 已知边界

- **依赖网关在线**：网关没起时插件无法工作（请求 502 / 命令连不上）。
- **gateway-key 由网管签发**：不能自己造，丢了在面板重签（regen）。
- **Caddy 认证分层（现网）**：`/v1/*` 靠 gateway-key 放行，`/admin/*` 在 Caddy 层有 basic_auth。
  插件命令若走公网域名调 `/admin/*`，需要控制面 API 在 Caddy 旁路 basic_auth（详见 REQ §3.1）——
  否则插件只能走本地回环地址 `http://127.0.0.1:8130` 或改用不带 basic_auth 的 `/admin/api/*` 路径。
- **模型清单两处维护**：网关 `/v1/models` 与插件注册的 models 需保持一致（后续可改为插件启动时从网关拉取）。
- **只服务于 Pi 宿主**：DSH 用同款架构的 `dsh-opencodego` 网关版（另行维护）。