docs: design.md v3-final (startup env + dynamic model picker, no settings)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
tao.chen
2026-07-23 15:56:07 +08:00
co-authored by Claude Fable 5
parent b5bcb8f67c
commit 1e42459560
+33 -44
View File
@@ -45,7 +45,12 @@
1. **原生 Cell Toolbar 单按钮(2026-07-23 v2 修正)**:不再自绘悬浮 toolbar,也合并了 v1 的三个 mode(优化/排错/编辑)为单个 `🪄 AI 智能编辑` 图标按钮。通过 `IToolbarWidgetRegistry.addFactory('Cell', 'opencode-cell-actions', ...)` 注入 JupyterLab 原生 cell toolbar,显示在 **active cell 右上角**move up/down 按钮旁,`rank: 100` 排在 delete 右侧);仅 `CodeCell` 显示,随 active cell 切换移动。**前端不做任何语义处理** —— 不再发 `mode` 字段,后端用统一的系统提示词 + 用户的自然语言指令 + 上下文(含 traceback)由 LLM 自己判断是优化/排错/编辑。 1. **原生 Cell Toolbar 单按钮(2026-07-23 v2 修正)**:不再自绘悬浮 toolbar,也合并了 v1 的三个 mode(优化/排错/编辑)为单个 `🪄 AI 智能编辑` 图标按钮。通过 `IToolbarWidgetRegistry.addFactory('Cell', 'opencode-cell-actions', ...)` 注入 JupyterLab 原生 cell toolbar,显示在 **active cell 右上角**move up/down 按钮旁,`rank: 100` 排在 delete 右侧);仅 `CodeCell` 显示,随 active cell 切换移动。**前端不做任何语义处理** —— 不再发 `mode` 字段,后端用统一的系统提示词 + 用户的自然语言指令 + 上下文(含 traceback)由 LLM 自己判断是优化/排错/编辑。
2. **Inline Prompt 输入框(v2 实现**:点击 `🪄 AI 智能编辑` 按钮后,`OpenCodeInlinePrompt` widget 追加到当前 `CodeCell` 的 DOM 末尾,呈现内嵌输入面板:多行 `textarea`placeholder "向 AI 描述你想要的修改…",无默认值)+ `🚀 发送` + `✕ 取消` 两个按钮。提交时前端仅收集 cell `source` / `outputs` / `error` 与用户在 textarea 里写的指令,POST `/opencode-bridge/edit`body 为 `{prompt, context, providerId?, modelId?}`**无 `mode`**)。**成功** → 前端调用 `cell.model.sharedModel.setSource(resp.finalSource)` 就地替换 cell 源码 + `Notification.info`**失败** → `Notification.error`,输入框保留(可改文案重发)。Session 由服务端 `SessionManager` 保证 1 notebook 1 sid(前端不感知)。 2. **Inline Prompt 输入框(v3-final**:点击 `🪄 AI 智能编辑` 按钮后,`OpenCodeInlinePrompt` widget 追加到当前 `CodeCell` 的 DOM 末尾,呈现内嵌输入面板:
* 顶部一行 `模型: <select>` —— 来自启动时 `GET /opencode-bridge/providers` 缓存的 `(provider, model)` 列表,**默认选中第一项**(v3-final 不再有任何 settings-based 默认),用户每次请求前可改。providers 拉取失败时该下拉框不渲染。
* 多行 `textarea`placeholder "向 AI 描述你想要的修改…",无默认值)。
* `🚀 发送` + `✕ 取消` 两个按钮。
提交时前端仅收集 cell `source` / `outputs` / `error` 与用户在 textarea 里写的指令,POST `/opencode-bridge/edit`body 为 `{prompt, context, providerId?, modelId?}`**无 `mode`**)。`providerId`/`modelId` 仅在下拉框有选中项时发出;下拉框不存在时省略,OpenCode Serve 用其默认。**成功** → 前端调用 `cell.model.sharedModel.setSource(resp.finalSource)` 就地替换 cell 源码 + `Notification.info`**失败** → `Notification.error`,输入框保留(可改文案重发)。Session 由服务端 `SessionManager` 保证 1 notebook 1 sid(前端不感知)。
3. **流式替换/Diff 预览** 3. **流式替换/Diff 预览**
@@ -277,57 +282,41 @@ v0.2.1 不实现自动重试,只 invalidate。前端看到 502 后可重发。
--- ---
## 🎛 6. Provider/Model 选择(v0.2.2 新增 ## ⚙️ 6. 启动配置 + 动态模型选择(v3-final2026-07-23
> **2026-07-22**:用户要求能在 settings 里选 OpenCode 的 provider 和 model,可选值从 `GET /config/providers` 动态获取 > **v3-final 修正**`opencode_bridge` 插件**完全不再使用 JupyterLab Settings Editor**`schema/plugin.json``properties: {}`)。所有 OpenCode Serve 连接参数在 `jupyter lab` 启动时通过环境变量指定;模型在 inline 输入框中动态选择(见 §2 item 2),无任何持久默认
### 6.1 Schema 扩展 ### 6.1 启动环境变量
`schema/plugin.json` 加 2 个 string 字段: | 变量 | 默认 | 说明 |
|---|---|---|
| `OPENCODE_BRIDGE_URL` | `http://127.0.0.1:4096` | OpenCode Serve 地址 |
| `OPENCODE_BRIDGE_USER` | `opencode` | HTTP Basic Auth 用户名 |
| `OPENCODE_BRIDGE_PASSWORD` | `""`(无 auth | HTTP Basic Auth 密码 |
| `OPENCODE_BRIDGE_TIMEOUT` | `120` | 请求 OpenCode Serve 超时(秒,整数) |
```json `opencode_bridge/config.py` `resolve_config` 统一解析,**不再**从 JupyterLab settings 字典兜底(`jupyter_settings` 参数保留仅为 API 兼容,实际未读)。
"opencodeProvider": {
"type": "string",
"title": "OpenCode Provider",
"description": "Provider id (如 'anthropic' / 'openai')。可选项由启动时从 /opencode-bridge/providers 拉取并打到 console 列出。留空 = 用 OpenCode 默认。",
"default": ""
},
"opencodeModel": {
"type": "string",
"title": "OpenCode Model",
"description": "Model id (如 'claude-sonnet-4-20250514')。可选项见 console 日志或 /opencode-bridge/providers。留空 = 用 provider 默认。",
"default": ""
}
```
### 6.2 选值如何给用户 ### 6.2 模型选择
JupyterLab settings 是静态 JSON schema**没有原生 dynamic enum**。v0.2.2 用最简方案 `opencodeProvider` / `opencodeModel` v0.2.2 settings 字段**已移除**。inline 输入框顶部 `<select>` 的选项来自 `index.ts` 启动时 `callOpenCodeProviders` 拉取并写入 `setOpenCodeProviders(p)` 的模块级缓存
- 启动时 fetch `/opencode-bridge/providers` → 把 provider/model 列表 `console.log` 出来
- 用户在 Settings Editor 里手填
- 后续 v0.4 考虑用 QuickPick 命令做交互式选择
### 6.3 数据流
``` ```
Settings (JupyterLab) index.ts (启动)
└─ GET /opencode-bridge/providers
├── opencodeProvider = "anthropic" └─ setOpenCodeProviders(data) // 模块级 _providers
└── opencodeModel = "claude-sonnet-4-20250514" └─ OpenCodeInlinePrompt 构造时读
└─ <select> 列出所有 (provider, model)
▼ setOpenCodeRuntime() └─ 用户选一项 → 提交时
Frontend (opencode_cell_footer.ts) └─ OpenCodeRequest.providerId / modelId
└─ POST /opencode-bridge/edit
▼ build request body
POST /opencode-bridge/edit
{ mode, prompt, context, providerId, modelId }
▼ send_message_sync
OpenCode Serve 接受 model = { providerID, modelID }
``` ```
### 6.4 不变量 - 默认选中:下拉框**第一项**(无 settings 可作默认)。
- 拉取失败(OpenCode Serve 未起)→ 缓存为 `null` → inline 不渲染 select → 提交时省略 `providerId`/`modelId`OpenCode Serve 用其默认。**优雅降级**。
- 留空字段("")→ 客户端不发 `providerId`/`modelId` → server 也不加 `model` 字段到 OpenCode 请求 → OpenCode 用默认 ### 6.3 行为变更(v0.2.2 → v3-final
- 用户在 settings 里设的值**不会自动回写到 OpenCode**——只是 request 级别的覆盖
- 改 settings 立即生效(下次 edit 请求就用新值);不需要重启 Jupyter - 之前 6 个 JupyterLab settings 字段(`opencodeServerUrl` / `User` / `Password` / `requestTimeoutSeconds` / `opencodeProvider` / `opencodeModel`)**全部失效**,需改用环境变量(连接类)或在 inline 输入框里挑(模型)。
- 连接配置从"可在 Settings Editor 改"变为"启动时定",更符合服务端连接参数的语义。
- 模型从"settings 持久默认"变为"每次手选",无持久化偏好。