mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-09 01:50:44 +08:00
feat(chat): image specialist legacy lane, Responses fixes, ACL + UI attachments
- Image expert: DashScope-style /chat/completions via image_legacy_client; early exit in direct_loop when skill_binding_role is image; shared placeholder helper; docs/IMAGE_SPECIALIST_LANE.md. - Strict attachment ACL: link_attachment_acl on assistant chat_message rows (sqlite_store); chat attachment rate limit when user_id empty; admin chat tests updated. - Admin chat UI: aggregate bubbles render assistant_text attachments (image_ref); WS expand path. - turn_runner: persisted_chat_attachments_nonempty for final_msg selection. - OpenAI Responses transport + agent_messages/agent_core_attempt adjustments; env docs and tests. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
d1bcc4debe
commit
faeb067856
21 changed files with 2516 additions and 331 deletions
|
|
@ -308,31 +308,68 @@
|
|||
- 作用:Gemini OpenAI 兼容下 tools 非流式开关
|
||||
- 生效:`oclaw/platform/llm/chat_models.py`
|
||||
|
||||
## OCR / 看多模态专线
|
||||
## OCR / 看图工具与降级专线(``AIA_OCR_*``)
|
||||
|
||||
用于工具 **`query_image_attachment`**、纯文本模型降级 OCR、**图片专家**单次看图与底层 **`send_ocr_image_messages`**:向 **OpenAI-compatible** 网关发送带图 `messages`(通常为 `POST …/chat/completions`)。旧版 DashScope 形态载荷见 **`send_legacy_image_messages`**(一般不再用于图片专家)。与主聊天模型配置的 Key/Base URL **互不继承**。
|
||||
仅用于:**`query_image_attachment`**、OpenAI-compatible **`send_ocr_image_messages`**、以及 **`openai_chat_completions`** 在纯文本模型上的多模态→OCR 降级。规范为 OpenAI **`image_url` + `messages`** Chat Completions 形态。**与下方「图片专家」专线互相独立**(不配齐不会自动复用另一端)。
|
||||
|
||||
- `AIA_OCR_BASE_URL`
|
||||
- 默认:无(必填,否则工具判为未配置)
|
||||
- 作用:多模态网关根地址(例如 `https://api.example.com/v1`;实际请求为 `BASE_URL` + `CHAT_ENDPOINT`)
|
||||
- 作用:OCR / 看图工具网关根地址(实际请求为 `BASE_URL` + `CHAT_ENDPOINT`)
|
||||
- 生效:`oclaw/platform/llm/image_ocr_client.py`
|
||||
|
||||
- `AIA_OCR_API_KEY`
|
||||
- 默认:无(必填)
|
||||
- 作用:上述网关的 Bearer API Key
|
||||
- 作用:上述网关 Bearer API Key
|
||||
- 生效:`oclaw/platform/llm/image_ocr_client.py`
|
||||
|
||||
- `AIA_OCR_MODEL`
|
||||
- 默认:无(必填,**不提供默认模型 id**)
|
||||
- 作用:网关上支持图+文的多模态模型名(由你的厂商决定)
|
||||
- 生效:`oclaw/platform/llm/image_ocr_client.py`,`oclaw/runtime/agents/specialist_agent.py`
|
||||
- 作用:支持 OCR/描述的 vision 模型 id
|
||||
- 生效:`oclaw/platform/llm/image_ocr_client.py`
|
||||
|
||||
- `AIA_OCR_CHAT_ENDPOINT`
|
||||
- 默认:`/chat/completions`
|
||||
- 作用:相对 `AIA_OCR_BASE_URL` 的路径
|
||||
- 生效:`oclaw/platform/llm/image_ocr_client.py`
|
||||
|
||||
**动态参数:** 代码中也可在调用 `send_ocr_image_messages` / `send_legacy_image_messages` 时传入 `base_url` / `api_key` / `model`,优先级高于环境变量(见 `vision_llm_backend_status` 仅在未传参时使用 env 判断「是否已配置」)。
|
||||
**动态参数:** 调用 `send_ocr_image_messages` 时传入 `base_url` / `api_key` / `model` 优先于上述环境变量;`vision_llm_backend_status` 仅检查 `AIA_OCR_*` 是否在未显式传参时可用。
|
||||
|
||||
---
|
||||
|
||||
## 图片专家专线(``AIA_IMAGE_EXPERT_*``)
|
||||
|
||||
**路由 specialist=`image`** 时由 **`send_legacy_image_messages`** 调用;可走 native `{"image"}`/`{"text"}` 或 compatible-mode **`image_url` + `text` 块状**载荷。**不使用 `AIA_OCR_*`**,也不在图片专家链路继承主会话模型的 Base URL/API Key。
|
||||
|
||||
- `AIA_IMAGE_EXPERT_BASE_URL`
|
||||
- 默认:无(必填,除非在代码中为 `send_legacy_image_messages(..., base_url=...)` 传入)
|
||||
- 作用:图片专家 HTTP 网关根路径(兼容 OpenAI multimodal 时常见 `https://dashscope.aliyuncs.com/compatible-mode/v1` 等)。
|
||||
- 生效:`oclaw/platform/llm/image_legacy_client.py`
|
||||
|
||||
- `AIA_IMAGE_EXPERT_API_KEY`
|
||||
- 默认:无(必填,除非显式传 `api_key`)
|
||||
- 作用:Bearer API Key(可与百炼「图片/多模态」文档中的 Key 一致;与 OCR Key **可不同**)。
|
||||
- 生效:`oclaw/platform/llm/image_legacy_client.py`
|
||||
|
||||
- `AIA_IMAGE_EXPERT_MODEL`
|
||||
- 默认:无(在用户界面已选会话模型时,`send_legacy_image_messages` **优先使用该模型的 `model` id**;仅当会话未带模型信息时才读本变量作补全)。
|
||||
- 作用:无 UI 会话模型/`model=` 可传时的专家默认模型 id(与 OCR 所用 VL **无需相同**)。
|
||||
- 生效:`oclaw/platform/llm/image_legacy_client.py`
|
||||
|
||||
- `AIA_IMAGE_EXPERT_CHAT_ENDPOINT`
|
||||
- 默认:`/chat/completions`
|
||||
- 作用:相对 `AIA_IMAGE_EXPERT_BASE_URL` 的路径。
|
||||
- 生效:`oclaw/platform/llm/image_legacy_client.py`
|
||||
|
||||
- `AIA_IMAGE_EXPERT_REQUEST_EXTRA`
|
||||
- 默认:无
|
||||
- 别名:`AIA_LEGACY_IMAGE_REQUEST_EXTRA`(旧名仍可读)。
|
||||
- 作用:发往图片专家请求的**顶层附加 JSON**。顶层 `model` / `messages` 仍由运行时代码覆盖。
|
||||
- 生效:`oclaw/platform/llm/image_legacy_client.py`
|
||||
|
||||
- `DASHSCOPE_IMAGE_STREAM` / `DASHSCOPE_IMAGE_N` / `DASHSCOPE_IMAGE_WATERMARK` / `DASHSCOPE_IMAGE_NEGATIVE_PROMPT` / `DASHSCOPE_IMAGE_PROMPT_EXTEND` / `DASHSCOPE_IMAGE_SIZE`
|
||||
- 默认:均未设置则不附加对应字段。
|
||||
- 作用:与 SDK 示例关键词对齐的零散变量;可被 `AIA_IMAGE_EXPERT_REQUEST_EXTRA`(或别名 JSON)中与同名键覆盖。
|
||||
- 生效:`oclaw/platform/llm/image_legacy_client.py`
|
||||
|
||||
> **说明:** 历史上曾使用 `AIA_IMAGE_BASE_URL` / `AIA_IMAGE_API_KEY` / `AIA_IMAGE_MODEL` / `AIA_IMAGE_CHAT_ENDPOINT` 作为同一路由;当前实现 **不再读取** 上述变量作 OCR 通道配置,请统一改为 `AIA_OCR_*`。(`AIA_IMAGE_TOOL_RESULT_REPLAY_CAP_CHARS` 等为 **另一用途**,与 OCR 网关无关,仍保留原名。)
|
||||
|
||||
|
|
|
|||
|
|
@ -60,13 +60,13 @@
|
|||
## 2026-05-10 / Unreleased
|
||||
|
||||
### Added
|
||||
- `AIA_OCR_BASE_URL`, `AIA_OCR_API_KEY`, `AIA_OCR_MODEL`, `AIA_OCR_CHAT_ENDPOINT`
|
||||
- 默认值:`CHAT_ENDPOINT` 为 `/chat/completions`,其余无默认(三项主体必填方视为已配置)
|
||||
- 用途:`query_image_attachment` / `send_ocr_image_messages` 等多模态 HTTP 通道命名(与主 LLM 分离)
|
||||
- 影响模块:`oclaw/platform/llm/image_ocr_client.py`、`image_legacy_client.py`,`oclaw/runtime/tools/public/query_image_attachment_tool.py`,`oclaw/runtime/agents/specialist_agent.py`(图片专家走 OCR 多模态 HTTP)
|
||||
- `AIA_IMAGE_EXPERT_API_KEY`、`AIA_IMAGE_EXPERT_BASE_URL`、`AIA_IMAGE_EXPERT_MODEL`、`AIA_IMAGE_EXPERT_CHAT_ENDPOINT`:图片专家(`send_legacy_image_messages`)专线,与 **`AIA_OCR_*`** 互不继承。
|
||||
- `AIA_IMAGE_EXPERT_REQUEST_EXTRA`:图片专家顶层 JSON;旧名 **`AIA_LEGACY_IMAGE_REQUEST_EXTRA`** 仍作别名可读。
|
||||
- `DASHSCOPE_IMAGE_*`(零散变量):由 `image_legacy_client` 映射为请求体顶层字段。
|
||||
- **`AIA_OCR_*`**(四项):仅存 **`query_image_attachment` / OCR 降级** 链路;已与图片专家链路拆分。
|
||||
|
||||
### Changed
|
||||
- `send_ocr_image_messages` / `send_legacy_image_messages` 不再有隐式默认模型 id;未配 `AIA_OCR_MODEL`(且未传 `model`)将直接失败
|
||||
- `send_ocr_image_messages` 未配 `AIA_OCR_MODEL`(且未传 `model`)失败;图片专家 **`send_legacy_image_messages`** 首选 **用户所选会话/专家绑定的模型的 `model`/`base_url`/`api_key`**,缺省时再回落 **`AIA_IMAGE_EXPERT_*`**(不读取 `AIA_OCR_*`);服务端若模型不支持看图则直接报错,不做备用 payload。
|
||||
|
||||
### Removed(OCR 通道)
|
||||
- `AIA_IMAGE_BASE_URL` / `AIA_IMAGE_API_KEY` / `AIA_IMAGE_MODEL` / `AIA_IMAGE_CHAT_ENDPOINT` **不再**作为看图/OCR 通道的环境变量读取(须改用 `AIA_OCR_*`)。与附件回放相关的 `AIA_IMAGE_TOOL_RESULT_REPLAY_CAP_CHARS` 等 **不受影响**。
|
||||
|
|
|
|||
84
docs/IMAGE_SPECIALIST_LANE.md
Normal file
84
docs/IMAGE_SPECIALIST_LANE.md
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
# 图片专家(Chat UI)专用链路
|
||||
|
||||
本文描述 **Admin `/chat` 选择「图片」专家** 时的端到端路径。这是一条 **与通用 Responses / 主对话模型环路隔离** 的特殊分支:仅在 `skill_binding_role == "image"` 时进入,其它专家或综合模式不受影响。
|
||||
|
||||
> 与本文无关:`docs/GATEWAY_IMAGE_GENERATE.md`(网关 RPC `image.generate`)、OCR 工具使用的 `image_ocr_client` 等。
|
||||
|
||||
---
|
||||
|
||||
## 1. 触发条件与隔离边界
|
||||
|
||||
| 条件 | 说明 |
|
||||
|------|------|
|
||||
| UI / Gateway | 用户在前端选择专家 **`image`**,请求体携带 `skill_binding_role`(或等价字段)为 **`image`**。 |
|
||||
| 入口守卫 | `runtime/direct_loop.py` 中 **`_maybe_image_specialist_legacy_gateway_turn`**:仅当 `skill_binding_role.lower() == "image"` 且未设置禁用开关时执行;否则返回 `None`,后续仍走常规 `run_oclaw_direct_loop`。 |
|
||||
| 禁用开关 | `AIA_IMAGE_SPECIALIST_DISABLE_LEGACY_GATEWAY_LANE=1`:关闭本 Early Return,图片专家改走与普通会话相同的模型/传输栈(用于调试或迁移)。 |
|
||||
|
||||
**不要在本链路外混用**:DashScope 形态的多模态 HTTP、`qwen-image*` 的空兼容响应回退等,均封装在 `platform/llm/image_legacy_client.py` 与 `image_http_common.py`,避免改到 `openai_responses` 的通用逻辑。
|
||||
|
||||
---
|
||||
|
||||
## 2. 运行时数据流(网关 → 落库)
|
||||
|
||||
1. **`run_oclaw_direct_loop`** 在用户消息落库后立刻调用 **`_maybe_image_specialist_legacy_gateway_turn`**。
|
||||
2. **输入附件**:`collect_legacy_lane_images_from_attachments` 将 UI 附件规范为 `data:` URL 或 HTTP URL(`image_ref` / `input_image` / `image_url` 等)。
|
||||
3. **无图**:直接写入一条 `assistant` / `assistant_text` 提示语并返回,不调用上游。
|
||||
4. **有图**:调用 **`send_legacy_image_messages`**(`/chat/completions` 兼容路径,非 Responses API)。
|
||||
5. **输出解析**:**`legacy_image_turn_bundle`**
|
||||
- 文本可为空;若有生成图则 **`materialize_legacy_response_output_attachments`** 写入本地 blob,产出 **`image_ref`**(或退化为 **`image_url`**)。
|
||||
6. **占位文案**:成功但只有图、无模型正文时,由 **`legacy_image_assistant_body_with_placeholder`**(`image_legacy_client`)写入中英文占位句;网关与 `specialist_agent` 共用,避免两处字符串分叉。
|
||||
7. **持久化**:**`store.add_message(role=assistant, event_type=assistant_text, attachments=…)`** —— 附件以 JSON 形式挂在助手消息上,而非 tool 行。
|
||||
|
||||
---
|
||||
|
||||
## 3. 与其它入口的差异
|
||||
|
||||
| 场景 | 模块 | 说明 |
|
||||
|------|------|------|
|
||||
| Chat 网关 + 图片专家 | `direct_loop._maybe_image_specialist_legacy_gateway_turn` | 上文主路径;Early Return,不进主 LLM 循环。 |
|
||||
| Specialist 编排临时会话 | `runtime/agents/specialist_agent.py` | 同样调用 `send_legacy_image_messages` / `legacy_image_turn_bundle`,逻辑对齐但不经过同一 Early Return。 |
|
||||
|
||||
两处共用 **`platform/llm/image_legacy_client.py`**,避免分叉实现。
|
||||
|
||||
---
|
||||
|
||||
## 4. 鉴权与下载(严格 ACL)
|
||||
|
||||
助手消息上的 **`image_ref`** 需在 **`attachment_acl`** 中有记录,才能在 **`AIA_ATTACHMENT_ACL_STRICT=1`** 下通过 **`GET /admin/api/chat/attachments/{id}`**。
|
||||
|
||||
- **`SqliteStore.add_message`** 会在落库后根据会话 **`ui_session_owner`** 对引用型附件执行 **`link_attachment_acl`**(与 tool 结果路径一致)。
|
||||
- 历史数据可用 **`POST /admin/api/chat/admin/attachments/acl/backfill`**(管理员)补齐。详见 **`docs/attachment-acl.md`**。
|
||||
|
||||
---
|
||||
|
||||
## 5. WebSocket 收口与前端展示
|
||||
|
||||
- **`interfaces/ws/turn_runner.py`**:`final_msg` 从最近一条非空助手消息组装;**`_persisted_chat_attachments_nonempty`** 需识别 **JSON 数组或单个 JSON 对象**,否则纯附件回合会选错行。
|
||||
- **`interfaces/admin/static/chat.js`**:聚合气泡 **`_buildAggregatedAssistantBubble`** 须对 **`assistant_text`** 片段调用 **`renderAttachmentsEl`**(不仅 `tool_result`),否则会出现「只有配文、无图」的现象。
|
||||
|
||||
---
|
||||
|
||||
## 6. 环境变量(索引)
|
||||
|
||||
详细列表与默认值以 **`docs/ENVIRONMENT_VARIABLES.md`** 为准。与本链路相关的典型前缀:
|
||||
|
||||
- **`AIA_IMAGE_EXPERT_*`** / **`AIA_IMAGE_SPECIALIST_*`**:图片专家 HTTP 基址、端点、模型、请求扩展等(与 **`AIA_OCR_*`** 分离)。
|
||||
- **`AIA_IMAGE_SPECIALIST_DISABLE_LEGACY_GATEWAY_LANE`**:禁用网关侧 legacy 专用线。
|
||||
- **`AIA_ATTACHMENT_ACL_STRICT`**:附件下载是否仅信任 ACL。
|
||||
|
||||
---
|
||||
|
||||
## 7. 测试与回归
|
||||
|
||||
- **`tests/test_image_legacy_gateway_lane.py`**:legacy 解析与规范化。
|
||||
- **`tests/test_attachment_acl_backfill.py`**:严格 ACL 下助手附件与 ACL 写入。
|
||||
- 修改 **`chat.js`** 聚合或 **`turn_runner`** final 消息逻辑后,应用图片专家跑一轮 **生成图 + 刷新历史** 做冒烟。
|
||||
|
||||
---
|
||||
|
||||
## 8. 变更原则(避免波及其它链路)
|
||||
|
||||
1. **默认改动范围**:`image_legacy_client.py`、`image_http_common.py`、`direct_loop` 中 **`_maybe_image_specialist_*` 函数体**、`specialist_agent` 中与 legacy image 调用相邻代码、`turn_runner` / `chat.js` 中与 **assistant + attachments** 展示相邻逻辑。
|
||||
2. **勿在** `openai_responses.py` **中为图片专家单独分支**,除非明确要做「非 legacy」通用能力。
|
||||
3. 新增开关优先 **`AIA_IMAGE_*` / `AIA_IMAGE_SPECIALIST_*`**,勿复用 OCR 变量。
|
||||
4. UI 层附件渲染:**assistant_text 与 tool_result** 对称处理引用型附件,避免只修一端。
|
||||
|
|
@ -30,6 +30,11 @@ Transport selection happens in `oclaw/runtime/agents/factory.py`.
|
|||
- **Streaming**: output text deltas via `on_token` → WS `chat.delta`
|
||||
- **Key**: profile secret or `OPENAI_API_KEY`
|
||||
|
||||
### Chat UI「图片专家」(绕行本矩阵)
|
||||
|
||||
- **Not** a separate profile transport: when the user selects specialist **`image`** in `/chat`, `runtime/direct_loop.py` takes an early return and calls **`platform/llm/image_legacy_client.send_legacy_image_messages`** (DashScope-style `/chat/completions`), so that turn does **not** use `OpenAIResponsesModel` / chat transports above.
|
||||
- Details, env vars, ACL, and UI hooks: **`docs/IMAGE_SPECIALIST_LANE.md`**.
|
||||
|
||||
### `anthropic` (Anthropic Messages streaming)
|
||||
- **Transport**: `oclaw/platform/llm/transports/anthropic_messages.py::AnthropicMessagesModel`
|
||||
- **API**: Anthropic `messages.stream` surface (gateway must provide Anthropic-compatible protocol)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue