docs: amend v3 spec to v3-final — remove all plugin settings, fully dynamic model picker

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
tao.chen
2026-07-23 13:14:41 +08:00
co-authored by Claude Fable 5
parent 8b51eda2ff
commit ca2281af3b
@@ -1,29 +1,31 @@
# Cell Toolbar Actions v3 — 启动时配置 + 模型选择器
# Cell Toolbar Actions v3 — 启动时配置 + 动态模型选择器
- 日期: 2026-07-23
- 状态: 已批准(用户于 2026-07-23 确认)
- 取代: v2 `OpenCodeSettings` 范围(commit d03f0fa)。本轮把 4 个 server 连接字段全部移出 JupyterLab Settings(改走启动 env),并在 inline 输入框增加模型选择器
- 状态: 已批准(用户于 2026-07-23 确认,v3-final: settings 一项不留)
- 取代: v2 `OpenCodeSettings` 类型 + v1/v2 全部 JupyterLab plugin settings。`opencode_bridge` 插件本轮**完全不再使用 JupyterLab Settings Editor**:所有连接配置走启动环境变量,模型选择完全动态(每次 inline 输入框里挑)
## 背景
v2 之后用户提了 2 个调整:
1. **`opencodeServerUrl` / `User` / `Password` / `requestTimeoutSeconds` 不再在 JupyterLab Settings Editor 里配** —— 全部在启动 jupyter server 时通过环境变量指定(`OPENCODE_BRIDGE_URL` / `_USER` / `_PASSWORD` / `_TIMEOUT`)。Settings Editor 只保留 `opencodeProvider` / `opencodeModel`(给模型选择器作默认选中)。
2. **inline 输入框增加模型选择器** —— 用户可以在每次请求时挑 provider/model,覆盖 settings 默认
1. **`opencodeServerUrl` / `User` / `Password` / `requestTimeoutSeconds` 不再在 JupyterLab Settings Editor 里配** —— 全部在启动 jupyter server 时通过环境变量指定。**随后**用户进一步要求:`opencodeProvider` / `opencodeModel` 这两项 settings 也不保留 —— 模型选择器**完全动态**,每次在 inline 输入框里挑,没有"设置默认值"的概念。**结果**:`schema/plugin.json` `properties` 变为 `{}`(只剩非 settings 的 schema 注释 `jupyter.lab.toolbars` 贡献)。
2. **inline 输入框增加模型选择器** —— `GET /opencode-bridge/providers` 启动时拉取并缓存;每次请求时用户挑一个 provider/model;无任何 settings 默认。
## 决策
| 维度 | 决策 |
|---|---|
| 启动 env(4 个) | `OPENCODE_BRIDGE_URL` / `_USER` / `_PASSWORD` / `_TIMEOUT`(秒,默认 120);`jupyter lab` 启动前 export;`config.py` `resolve_config``os.environ.get(ENV) or DEFAULT`,不再从 JupyterLab settings 兜底 |
| JupyterLab Settings(只剩 2 个) | `opencodeProvider` / `opencodeModel` —— 给模型选择器作默认选中 |
| `OpenCodeSettings` 前端类型 | 缩为 `{ opencodeProvider: string; opencodeModel: string }` |
| 启动 env(4 个) | `OPENCODE_BRIDGE_URL` / `_USER` / `_PASSWORD` / `_TIMEOUT`(秒,默认 120);`jupyter lab` 启动前 export;`config.py` `resolve_config``os.environ.get(ENV) or DEFAULT`,**完全不再**从 JupyterLab settings 兜底(没有 settings 可读) |
| JupyterLab Settings | **无**`schema/plugin.json` `properties: {}`;`opencode_bridge` 插件不再 `optional: [ISettingRegistry]`;`settingRegistry.load(...)` / `setOpenCodeRuntime({settings})` 整段删除 |
| 前端 `OpenCodeSettings` 类型 | **删除整个类型**;`DEFAULT_OPENCODE_SETTINGS` / `readOpenCodeSettings` 删除;`opencode_bridge.spec.ts` 中相关测试删除 |
| `setOpenCodeRuntime` | 改名为 `setOpenCodeServerSettings(serverSettings)`,只接收 `ServerConnection.ISettings`(Jupyter 自己的 serverSettings,用于 `callOpenCodeEdit` 拼 URL);不再有 `_settings` 模块级 |
| `index.ts` | `optional: [IToolbarWidgetRegistry]`(去掉 `ISettingRegistry`);激活时:注册 toolbar factory + 拉 providers 写缓存(无 settings block) |
| 模型选择器 UI | inline 面板顶部加一行 `模型: <select>`(单个下拉),选项 label `provider / model`,value `providerId\|modelId`(`\|` 分隔,IDs 不含此字符);textarea 在其下方 |
| 默认选中 | 下拉框默认选中 `settings.opencodeProvider` / `settings.opencodeModel` 拼成的项;若无对应项,默认第一项 |
| 提交覆盖 | prompt 的 `onSubmit(text, providerId, modelId)` 把当前选中带回去;actions 用它作为 request body 的 `providerId`/`modelId`(覆盖 settings 默认) |
| Providers 数据来源 | `GET /opencode-bridge/providers` `index.ts` 启动时拉取;本轮加一个 `setOpenCodeProviders(p)` 把结果写到 `opencode_cell_actions.ts` 模块级缓存,inline prompt 构造时读 |
| Providers 拉取失败 | 缓存为 `null`,inline prompt **不渲染** select;提交走 settings 兜底(settings 没设就不发 `providerId`/`modelId`,OpenCode 用默认)。**优雅降级** |
| schema | 顶层 `jupyter.lab.toolbars` 贡献不变(item 名 `opencode-cell-actions`,rank 100) |
| 默认选中 | 下拉框**第一项**(无 settings 可作默认) |
| 提交覆盖 | prompt 的 `onSubmit(text, providerId, modelId)` 把当前选中带回去;actions 用它作为 request body 的 `providerId`/`modelId`( settings 兜底,prompt 必须有选择) |
| Providers 数据来源 | `GET /opencode-bridge/providers``index.ts` 启动时拉取;通过新 `setOpenCodeProviders(p)` 写到 `opencode_cell_actions.ts` 模块级 `_providers` 缓存;inline prompt 构造时读 |
| Providers 拉取失败 | 缓存为 `null`,inline prompt **不渲染** select;提交`providerId`/`modelId``undefined`**不发**这两个字段(OpenCode Serve 用它自己的默认)。**优雅降级** |
| schema `jupyter.lab.toolbars` | 顶层 `jupyter.lab.toolbars: { "Cell": [{ "name": "opencode-cell-actions", "rank": 100 }] }` 保留(toolbar item 仍需 schema 贡献) |
## 接口契约
@@ -31,13 +33,14 @@ v2 之后用户提了 2 个调整:
```json
{ "prompt": "...", "context": { ... }, "providerId": "...", "modelId": "..." }
```
- `providerId` / `modelId` 可选;**只在** inline 选择器有选中项时发;providers 拉取失败时不发。
**后端**:与 v2 相同。`config.py` 新增:
```python
ENV_TIMEOUT = "OPENCODE_BRIDGE_TIMEOUT"
DEFAULT_REQUEST_TIMEOUT = 120
```
`resolve_config` 4 个字段全部走 `os.environ.get(ENV) or DEFAULT`,**不再** `bridge.get(...)` 兜底(settings 字典里也不再含这 4 个 key)`requestTimeoutSeconds` 是 server-side(OpenCodeClient → OpenCode Serve 的 httpx timeout),不是前端
`resolve_config` 4 个字段全部走 `os.environ.get(ENV) or DEFAULT``requestTimeoutSeconds` 是 server-side(OpenCodeClient → OpenCode Serve 的 httpx timeout),与前端无关
**环境变量清单**:
| 变量 | 默认 | 说明 |
@@ -49,19 +52,23 @@ DEFAULT_REQUEST_TIMEOUT = 120
## 机制
- **前端**:`OpenCodeCellActions` 多了个模块级 `_providers` 引用(由 `setOpenCodeProviders` 写入,启动时 `callOpenCodeProviders` 成功后调);构造 `OpenCodeInlinePrompt` 时把 providers + settings 默认 provider/model 传进去。点击提交 → prompt 回调带选中的 provider/model → actions 写进 `OpenCodeRequest``providerId`/`modelId` 字段。
- **服务端**:`config.py` 完全不再读 JupyterLab settings 字典里这 4 个 key(它们已经不存在了);SessionManager / EditHandler 不变
- **前端**:
- `opencode_cell_actions.ts` 模块级:`_providers`(`setOpenCodeProviders` 写入)、`_serverSettings`(`setOpenCodeServerSettings` 写入)。**无** `_settings`
- `OpenCodeInlinePrompt` 构造接收 `providers`(必填,可能为 `null`);无 `defaultProviderId`/`defaultModelId` 参数;默认选中第一项。
- 点击提交 → prompt 回调 `(text, providerId?, modelId?)`;actions 用回传的 provider/model 写进 `OpenCodeRequest`(`undefined` → 不发)。
- **服务端**:`config.py` 不再读 JupyterLab settings 字典(`handler.settings.get("opencode_bridge", {})` 返回 `{}`);SessionManager / EditHandler 不变。
- **OpenCode**:与 v2 相同。
## 兼容性
- `OpenCodeSettings` 类型字段减少 4 个;前端任何引用这些字段的地方需清理(grep)
- 之前在 JupyterLab Settings Editor 里设过这 4 个值的用户:不再生效,需改用环境变量。**行为变更,需在 changelog/release note 标注**(本次不修 release 文件,但 design.md §6 写清楚)。
- `requestTimeoutSeconds` 行为不变(只是配置入口换了),现有 pytest 兼容
- **`opencode_bridge` 插件不再读 JupyterLab settings**;之前在 Settings Editor 设过 6 个字段(`opencodeServerUrl` / `User` / `Password` / `requestTimeoutSeconds` / `opencodeProvider` / `opencodeModel`)的用户:**全部失效**,需改用环境变量(连接类)或在 inline 输入框里挑(模型)。**行为变更,需在 release note 标注**
- 4 个 env 变量 + 默认值覆盖之前 6 个 settings 字段的所有功能;模型选择器替代 `opencodeProvider`/`opencodeModel` settings 的"默认"作用(但完全由用户每次手选,无持久默认)。
- `OpenCodeSettings` / `DEFAULT_OPENCODE_SETTINGS` / `readOpenCodeSettings` 类型/常量/函数整组删除;`opencode_bridge.spec.ts` 中相关测试删除
- `setOpenCodeRuntime` 重命名为 `setOpenCodeServerSettings`(签名 `{ serverSettings }`),所有调用方更新。
## 验证
- `pytest opencode_bridge/tests/` 全过(`test_edit_handler` 改动:新 `config.py` 仍能解析 env+default)
- `jlpm test` 全过(单测覆盖: select 渲染 N 项 / 默认选中 settings 值 / submit 覆盖 / providers 降级)
- `pytest opencode_bridge/tests/` 全过
- `jlpm test` 全过(单测覆盖: select 渲染 N 项 / 默认第一项 / submit 覆盖 / providers 降级)
- `jlpm build` 成功
- 手动:`jupyter lab` 启动,active cell 右上角单按钮 → inline 面板顶部出现 `模型: [select]` 下拉(列出 OpenCode Serve 的所有 provider/model),默认选中 settings 值;切换选择 → 发送 → request 带新 provider/model
- 手动:`export OPENCODE_BRIDGE_URL=... && jupyter lab` active cell 右上角单按钮 → inline 面板顶部 `模型: [select]`(列出 provider/model,默认第一项) → 切换选择 → 发送 → request 带新 provider/model;Settings Editor 中 `opencode_bridge` 无任何字段