Use case or problem
我希望在 Pi 中使用以下模型组合:
- 主模型:
opencode-go/deepseek-v4-flash
- 辅助视觉模型:
opencode-go/qwen3.7-plus
- 同时保留 Pi 中已登录的 Codex、Grok 等原生多模态模型
当前已经会根据:
ctx?.model?.input?.includes?.("image")
旁路原生多模态模型,因此本请求不涉及更改 Codex、Grok 等模型的原生图片输入,也不涉及模型路由冲突。
当前限制在于辅助视觉模型的 API 格式。
目前Pi extension 和 OpenCode plugin 中的视觉请求实现均固定使用 OpenAI Chat Completions 格式:
POST {VISION_BASE_URL}/chat/completions
图片使用:
{
"type": "image_url",
"image_url": {
"url": "data:image/png;base64,..."
}
}
响应则固定从以下路径读取:
choices[0].message.content
但是 OpenCode Go及部分其它运营商当前为 Qwen3.7 Plus 提供的是 Anthropic Messages-compatible endpoint:
POST https://opencode.ai/zen/go/v1/messages
模型 ID:
OpenCode Go 的 endpoint 表:
https://github.com/anomalyco/opencode/blob/dev/packages/web/src/content/docs/go.mdx#endpoints
例如,以下配置目前无法通过 OpenCode Go 文档中为该模型提供的 API 格式工作:
VISION_API_KEY=<redacted>
VISION_BASE_URL=https://opencode.ai/zen/go/v1
VISION_MODEL=qwen3.7-plus
当 Pi 的文本模型收到图片时,extension 会把请求发送到:
https://opencode.ai/zen/go/v1/chat/completions
而不是该模型对应的:
https://opencode.ai/zen/go/v1/messages
同时,请求图片块和响应结构也分别属于 OpenAI 与 Anthropic 两种不同格式。
这不仅影响 Pi extension,也影响所有复用 vision_client.py 的联网视觉工具,例如 glance,以及包含相同 describe core 的 OpenCode plugin。
Expected behavior
允许用户显式选择视觉后端使用的 API 格式,使以下组合可以工作:
Pi primary model:
opencode-go/deepseek-v4-flash
Vision backend:
opencode-go/qwen3.7-plus
Vision API format:
anthropic-messages
当切换到支持图片输入的 Codex、Grok 或其他原生多模态模型时,Pi extension 仍应保持当前行为:不改写图片,也不调用辅助视觉 API。
Actual behavior
视觉客户端固定构造 OpenAI /chat/completions 请求,因此只能使用支持 OpenAI Chat Completions 与 image_url 格式的视觉 endpoint,无法使用仅暴露 Anthropic /messages 格式的视觉模型。
Proposed change
建议增加一个显式、向后兼容的视觉 API 格式配置,例如:
# Existing behavior and default
VISION_API_FORMAT=openai-chat-completions
或:
VISION_API_FORMAT=anthropic-messages
变量名称可以按项目现有命名习惯调整;重点是由用户明确选择格式,而不是根据模型名称进行隐式推断。
openai-chat-completions
保持当前行为不变:
POST {VISION_BASE_URL}/chat/completions
请求使用:
{
"model": "...",
"max_tokens": 4096,
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "..."
},
{
"type": "image_url",
"image_url": {
"url": "data:image/png;base64,..."
}
}
]
}
]
}
解析:
choices[0].message.content
anthropic-messages
使用:
POST {VISION_BASE_URL}/messages
将 data URL 转换为 Anthropic image source block,例如:
{
"model": "qwen3.7-plus",
"max_tokens": 4096,
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "..."
},
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": "..."
}
}
]
}
]
}
解析响应中的文本块:
{
"content": [
{
"type": "text",
"text": "..."
}
]
}
即合并所有:
鉴权 header 可以遵循 Anthropic-compatible endpoint 的要求,同时继续保证:
- API key 不会进入日志
- API key 在错误正文中被替换为
<redacted>
- 请求失败时不会静默返回伪造的图片描述
- 当前重试和超时策略保持不变
Suggested implementation scope
建议把协议差异限制在较小的 request/response adapter 中,例如:
build request URL
build request headers
build request payload
parse response text
然后在以下实现中复用相同语义:
vision_client.py
extensions/pi/vision.ts
extensions/opencode/vision.ts
Pi 和 OpenCode extension 仍可保持单文件、自包含,不需要引入 SDK 或额外运行时依赖。
本功能只需要支持视觉描述所需的最小 Anthropic Messages 子集:
- 非流式请求
- 一个 user message
- text blocks
- image blocks
- 一个或多个图片
- text response blocks
不需要同时实现:
- tool use
- thinking blocks
- prompt caching
- streaming
- 完整 Anthropic SDK 行为
Backward compatibility
当 VISION_API_FORMAT 未设置时,应继续默认使用当前的 OpenAI Chat Completions 格式。因此已有用户的配置和行为不需要变化。
该变更也不应修改:
- Pi 对原生多模态模型的旁路逻辑
- 当前 focus-hint 行为
- 图片描述缓存
- stored session 中的原始图片
- CLI 与 proxy 的边界
- 用户的 Pi/OpenCode 登录凭证
- 模型名称或 provider 配置
本请求也不要求 toolkit 自动读取 Pi 的 auth.json。继续由用户通过 VISION_API_KEY 显式提供凭证即可。
Suggested acceptance criteria
Primary area
Other
Alternatives considered
1. 使用另一个 OpenAI-compatible 视觉模型
这可以绕过问题,但无法使用已经通过 OpenCode Go 或部分其他订阅获得的 Qwen3.7 Plus,也限制了 toolkit 可使用的视觉提供商。
2. 在本地额外运行 OpenAI-to-Anthropic 转换代理
技术上可行,但会增加:
- 一个额外的后台服务
- 端口和进程管理
- 额外的错误和日志层
- 安装与配置复杂度
而且无法自然覆盖 Pi extension、OpenCode plugin 和独立 CLI 的所有使用方式。
3. 让 Pi extension 直接通过 Pi 的 provider abstraction 调用 Qwen
这可以解决 Pi 单一宿主中的问题,但会使 extension 强依赖 Pi 的内部模型执行 API,也不能解决 glance、proxy 和 OpenCode plugin 使用 Anthropic-compatible 视觉 endpoint 的需求。
4. 根据模型名自动推断 API 格式
例如看到 qwen3.7-plus 就自动选择 Anthropic Messages。该方案比较脆弱,因为同一个模型可能由不同 provider 通过不同协议提供。
显式的 VISION_API_FORMAT 配置更可预测,也更容易测试。
Scope checks
Use case or problem
我希望在 Pi 中使用以下模型组合:
opencode-go/deepseek-v4-flashopencode-go/qwen3.7-plus当前已经会根据:
旁路原生多模态模型,因此本请求不涉及更改 Codex、Grok 等模型的原生图片输入,也不涉及模型路由冲突。
当前限制在于辅助视觉模型的 API 格式。
目前Pi extension 和 OpenCode plugin 中的视觉请求实现均固定使用 OpenAI Chat Completions 格式:
图片使用:
{ "type": "image_url", "image_url": { "url": "data:image/png;base64,..." } }响应则固定从以下路径读取:
但是 OpenCode Go及部分其它运营商当前为 Qwen3.7 Plus 提供的是 Anthropic Messages-compatible endpoint:
模型 ID:
OpenCode Go 的 endpoint 表:
https://github.com/anomalyco/opencode/blob/dev/packages/web/src/content/docs/go.mdx#endpoints
例如,以下配置目前无法通过 OpenCode Go 文档中为该模型提供的 API 格式工作:
当 Pi 的文本模型收到图片时,extension 会把请求发送到:
而不是该模型对应的:
同时,请求图片块和响应结构也分别属于 OpenAI 与 Anthropic 两种不同格式。
这不仅影响 Pi extension,也影响所有复用
vision_client.py的联网视觉工具,例如glance,以及包含相同 describe core 的 OpenCode plugin。Expected behavior
允许用户显式选择视觉后端使用的 API 格式,使以下组合可以工作:
当切换到支持图片输入的 Codex、Grok 或其他原生多模态模型时,Pi extension 仍应保持当前行为:不改写图片,也不调用辅助视觉 API。
Actual behavior
视觉客户端固定构造 OpenAI
/chat/completions请求,因此只能使用支持 OpenAI Chat Completions 与image_url格式的视觉 endpoint,无法使用仅暴露 Anthropic/messages格式的视觉模型。Proposed change
建议增加一个显式、向后兼容的视觉 API 格式配置,例如:
或:
变量名称可以按项目现有命名习惯调整;重点是由用户明确选择格式,而不是根据模型名称进行隐式推断。
openai-chat-completions保持当前行为不变:
请求使用:
{ "model": "...", "max_tokens": 4096, "messages": [ { "role": "user", "content": [ { "type": "text", "text": "..." }, { "type": "image_url", "image_url": { "url": "data:image/png;base64,..." } } ] } ] }解析:
anthropic-messages使用:
将 data URL 转换为 Anthropic image source block,例如:
{ "model": "qwen3.7-plus", "max_tokens": 4096, "messages": [ { "role": "user", "content": [ { "type": "text", "text": "..." }, { "type": "image", "source": { "type": "base64", "media_type": "image/png", "data": "..." } } ] } ] }解析响应中的文本块:
{ "content": [ { "type": "text", "text": "..." } ] }即合并所有:
鉴权 header 可以遵循 Anthropic-compatible endpoint 的要求,同时继续保证:
<redacted>Suggested implementation scope
建议把协议差异限制在较小的 request/response adapter 中,例如:
然后在以下实现中复用相同语义:
vision_client.pyextensions/pi/vision.tsextensions/opencode/vision.tsPi 和 OpenCode extension 仍可保持单文件、自包含,不需要引入 SDK 或额外运行时依赖。
本功能只需要支持视觉描述所需的最小 Anthropic Messages 子集:
不需要同时实现:
Backward compatibility
当
VISION_API_FORMAT未设置时,应继续默认使用当前的 OpenAI Chat Completions 格式。因此已有用户的配置和行为不需要变化。该变更也不应修改:
本请求也不要求 toolkit 自动读取 Pi 的
auth.json。继续由用户通过VISION_API_KEY显式提供凭证即可。Suggested acceptance criteria
VISION_API_FORMAT时,现有 OpenAI-compatible 配置和测试全部保持原行为。VISION_API_FORMAT=anthropic-messages时,请求发送到{VISION_BASE_URL}/messages。content[].text。tests/test_vision_client.py增加两个 API 格式的 stubbed request/response 测试。tests/test_extensions.mjs增加 Pi 和 OpenCode extension 的 Anthropic Messages 测试。.env.example、README.md、README_CN.md和 extension README 记录该配置。Primary area
Other
Alternatives considered
1. 使用另一个 OpenAI-compatible 视觉模型
这可以绕过问题,但无法使用已经通过 OpenCode Go 或部分其他订阅获得的 Qwen3.7 Plus,也限制了 toolkit 可使用的视觉提供商。
2. 在本地额外运行 OpenAI-to-Anthropic 转换代理
技术上可行,但会增加:
而且无法自然覆盖 Pi extension、OpenCode plugin 和独立 CLI 的所有使用方式。
3. 让 Pi extension 直接通过 Pi 的 provider abstraction 调用 Qwen
这可以解决 Pi 单一宿主中的问题,但会使 extension 强依赖 Pi 的内部模型执行 API,也不能解决
glance、proxy 和 OpenCode plugin 使用 Anthropic-compatible 视觉 endpoint 的需求。4. 根据模型名自动推断 API 格式
例如看到
qwen3.7-plus就自动选择 Anthropic Messages。该方案比较脆弱,因为同一个模型可能由不同 provider 通过不同协议提供。显式的
VISION_API_FORMAT配置更可预测,也更容易测试。Scope checks