fix: use available qianfan vision model

This commit is contained in:
jimmyzhuu
2026-05-06 13:34:39 +08:00
parent 76e6b7b471
commit fb7962c7f2
11 changed files with 39 additions and 39 deletions

View File

@@ -609,7 +609,7 @@ API Key 创建:在 [控制台](https://aistudio.google.com/app/apikey?hl=zh-cn
} }
``` ```
- `model`: 默认推荐填写 `ernie-5.0`,也可填写 `ernie-4.5-turbo-128k``ernie-4.5-turbo-32k``ernie-x1-turbo-32k`Vision 工具可使用 `ernie-4.5-turbo-vl-preview` - `model`: 默认推荐填写 `ernie-5.0`,也可填写 `ernie-4.5-turbo-128k``ernie-4.5-turbo-32k``ernie-x1-turbo-32k`Vision 工具可使用 `ernie-4.5-turbo-vl`
- `qianfan_api_key`: 百度千帆 API Key通常以 `bce-v3/` 开头,可在百度智能云控制台创建 - `qianfan_api_key`: 百度千帆 API Key通常以 `bce-v3/` 开头,可在百度智能云控制台创建
- `qianfan_api_base`: 可选,默认为 `https://qianfan.baidubce.com/v2` - `qianfan_api_base`: 可选,默认为 `https://qianfan.baidubce.com/v2`

View File

@@ -53,7 +53,7 @@ _DISCOVERABLE_MODELS = [
("ark_api_key", const.DOUBAO, const.DOUBAO_SEED_2_PRO, "Doubao"), ("ark_api_key", const.DOUBAO, const.DOUBAO_SEED_2_PRO, "Doubao"),
("dashscope_api_key", const.QWEN_DASHSCOPE, const.QWEN36_PLUS, "DashScope"), ("dashscope_api_key", const.QWEN_DASHSCOPE, const.QWEN36_PLUS, "DashScope"),
("claude_api_key", const.CLAUDEAPI, const.CLAUDE_4_6_SONNET, "Claude"), ("claude_api_key", const.CLAUDEAPI, const.CLAUDE_4_6_SONNET, "Claude"),
("qianfan_api_key", const.QIANFAN, const.ERNIE_45_TURBO_VL_PREVIEW, "Qianfan"), ("qianfan_api_key", const.QIANFAN, const.ERNIE_45_TURBO_VL, "Qianfan"),
("gemini_api_key", const.GEMINI, const.GEMINI_31_FLASH_LITE_PRE, "Gemini"), ("gemini_api_key", const.GEMINI, const.GEMINI_31_FLASH_LITE_PRE, "Gemini"),
("zhipu_ai_api_key", const.ZHIPU_AI, const.GLM_4_7, "ZhipuAI"), ("zhipu_ai_api_key", const.ZHIPU_AI, const.GLM_4_7, "ZhipuAI"),
("minimax_api_key", const.MiniMax, const.MINIMAX_M2_7, "MiniMax"), ("minimax_api_key", const.MiniMax, const.MINIMAX_M2_7, "MiniMax"),
@@ -142,7 +142,7 @@ class Vision(BaseTool):
"Error: No model available for Vision.\n" "Error: No model available for Vision.\n"
"The main model does not support vision and no other API keys are configured.\n" "The main model does not support vision and no other API keys are configured.\n"
"Options:\n" "Options:\n"
" 1. Switch to a multimodal model (e.g. ernie-4.5-turbo-vl-preview, qwen3.6-plus, claude-sonnet-4-6, gemini-2.0-flash)\n" " 1. Switch to a multimodal model (e.g. ernie-4.5-turbo-vl, qwen3.6-plus, claude-sonnet-4-6, gemini-2.0-flash)\n"
" 2. Configure OPENAI_API_KEY: env_config(action=\"set\", key=\"OPENAI_API_KEY\", value=\"your-key\")\n" " 2. Configure OPENAI_API_KEY: env_config(action=\"set\", key=\"OPENAI_API_KEY\", value=\"your-key\")\n"
" 3. Configure LINKAI_API_KEY: env_config(action=\"set\", key=\"LINKAI_API_KEY\", value=\"your-key\")" " 3. Configure LINKAI_API_KEY: env_config(action=\"set\", key=\"LINKAI_API_KEY\", value=\"your-key\")"
) )

View File

@@ -92,8 +92,8 @@ ERNIE_45_TURBO_128K = "ernie-4.5-turbo-128k"
ERNIE_45_TURBO_32K = "ernie-4.5-turbo-32k" ERNIE_45_TURBO_32K = "ernie-4.5-turbo-32k"
ERNIE_X1_TURBO_32K = "ernie-x1-turbo-32k" ERNIE_X1_TURBO_32K = "ernie-x1-turbo-32k"
ERNIE_4_TURBO_8K = "ERNIE-4.0-Turbo-8K" ERNIE_4_TURBO_8K = "ERNIE-4.0-Turbo-8K"
ERNIE_45_TURBO_VL_PREVIEW = "ernie-4.5-turbo-vl-preview" ERNIE_45_TURBO_VL = "ernie-4.5-turbo-vl"
ERNIE_45_VL_28B_A3B = "ernie-4.5-vl-28b-a3b" ERNIE_45_TURBO_VL_32K = "ernie-4.5-turbo-vl-32k"
# Qwen (通义千问 - 阿里云 DashScope) # Qwen (通义千问 - 阿里云 DashScope)
QWEN_TURBO = "qwen-turbo" QWEN_TURBO = "qwen-turbo"
@@ -171,7 +171,7 @@ MODEL_LIST = [
# Baidu Qianfan / ERNIE # Baidu Qianfan / ERNIE
QIANFAN, ERNIE_5, ERNIE_45_TURBO_128K, ERNIE_45_TURBO_32K, ERNIE_X1_TURBO_32K, ERNIE_4_TURBO_8K, QIANFAN, ERNIE_5, ERNIE_45_TURBO_128K, ERNIE_45_TURBO_32K, ERNIE_X1_TURBO_32K, ERNIE_4_TURBO_8K,
ERNIE_45_TURBO_VL_PREVIEW, ERNIE_45_VL_28B_A3B, ERNIE_45_TURBO_VL, ERNIE_45_TURBO_VL_32K,
# MiniMax # MiniMax
MiniMax, MINIMAX_M2_7, MINIMAX_M2_7_HIGHSPEED, MINIMAX_M2_5, MINIMAX_M2_1, MINIMAX_M2_1_LIGHTNING, MINIMAX_M2, MINIMAX_ABAB6_5, MiniMax, MINIMAX_M2_7, MINIMAX_M2_7_HIGHSPEED, MINIMAX_M2_5, MINIMAX_M2_1, MINIMAX_M2_1_LIGHTNING, MINIMAX_M2, MINIMAX_ABAB6_5,

View File

@@ -30,13 +30,13 @@ Option 1: Native integration (recommended):
## Vision tool ## Vision tool
After `qianfan_api_key` is configured, Agent mode can auto-discover Qianfan for the Vision tool. The recommended Qianfan vision model is `ernie-4.5-turbo-vl-preview`: After `qianfan_api_key` is configured, Agent mode can auto-discover Qianfan for the Vision tool. The recommended Qianfan vision model is `ernie-4.5-turbo-vl`:
```json ```json
{ {
"tool": { "tool": {
"vision": { "vision": {
"model": "ernie-4.5-turbo-vl-preview" "model": "ernie-4.5-turbo-vl"
} }
} }
} }

View File

@@ -23,7 +23,7 @@ If the current provider fails, the tool automatically tries the next one until i
| Vendor | Vision Model | Notes | | Vendor | Vision Model | Notes |
| --- | --- | --- | | --- | --- | --- |
| OpenAI / Compatible | Main model | All OpenAI-compatible multimodal models | | OpenAI / Compatible | Main model | All OpenAI-compatible multimodal models |
| Baidu Qianfan | ernie-4.5-turbo-vl-preview | Auto-discovered when `qianfan_api_key` is configured; can also be selected via `tool.vision.model` | | Baidu Qianfan | ernie-4.5-turbo-vl | Auto-discovered when `qianfan_api_key` is configured; can also be selected via `tool.vision.model` |
| Qwen (DashScope) | Main model | Via MultiModalConversation API | | Qwen (DashScope) | Main model | Via MultiModalConversation API |
| Claude | Main model | Anthropic native image format | | Claude | Main model | Anthropic native image format |
| Gemini | Main model | inlineData format | | Gemini | Main model | inlineData format |
@@ -53,7 +53,7 @@ To specify a particular model for the vision tool, add to `config.json`:
{ {
"tool": { "tool": {
"vision": { "vision": {
"model": "ernie-4.5-turbo-vl-preview" "model": "ernie-4.5-turbo-vl"
} }
} }
} }

View File

@@ -30,13 +30,13 @@ description: Baidu Qianfan ERNIE モデル設定
## Vision ツール ## Vision ツール
`qianfan_api_key` を設定すると、Agent モードの Vision ツールは Qianfan を自動検出できます。推奨する Qianfan の視覚モデルは `ernie-4.5-turbo-vl-preview` です: `qianfan_api_key` を設定すると、Agent モードの Vision ツールは Qianfan を自動検出できます。推奨する Qianfan の視覚モデルは `ernie-4.5-turbo-vl` です:
```json ```json
{ {
"tool": { "tool": {
"vision": { "vision": {
"model": "ernie-4.5-turbo-vl-preview" "model": "ernie-4.5-turbo-vl"
} }
} }
} }

View File

@@ -23,7 +23,7 @@ Vision ツールは多段階の自動選択+自動フォールバック戦略
| ベンダー | ビジョンモデル | 説明 | | ベンダー | ビジョンモデル | 説明 |
| --- | --- | --- | | --- | --- | --- |
| OpenAI / 互換プロトコル | メインモデル | すべての OpenAI 互換マルチモーダルモデルに対応 | | OpenAI / 互換プロトコル | メインモデル | すべての OpenAI 互換マルチモーダルモデルに対応 |
| Baidu Qianfan | ernie-4.5-turbo-vl-preview | `qianfan_api_key` を設定すると自動検出され、`tool.vision.model` でも指定できます | | Baidu Qianfan | ernie-4.5-turbo-vl | `qianfan_api_key` を設定すると自動検出され、`tool.vision.model` でも指定できます |
| 通義千問 (DashScope) | メインモデル | MultiModalConversation API 経由 | | 通義千問 (DashScope) | メインモデル | MultiModalConversation API 経由 |
| Claude | メインモデル | Anthropic ネイティブ画像形式 | | Claude | メインモデル | Anthropic ネイティブ画像形式 |
| Gemini | メインモデル | inlineData 形式 | | Gemini | メインモデル | inlineData 形式 |
@@ -53,7 +53,7 @@ Vision ツールで使用するモデルを指定するには、`config.json`
{ {
"tool": { "tool": {
"vision": { "vision": {
"model": "ernie-4.5-turbo-vl-preview" "model": "ernie-4.5-turbo-vl"
} }
} }
} }

View File

@@ -30,13 +30,13 @@ description: 百度千帆 ERNIE 模型配置
## Vision 工具 ## Vision 工具
配置 `qianfan_api_key` 后Agent 的 Vision 工具可以自动使用千帆视觉模型。默认推荐使用 `ernie-4.5-turbo-vl-preview` 配置 `qianfan_api_key` 后Agent 的 Vision 工具可以自动使用千帆视觉模型。默认推荐使用 `ernie-4.5-turbo-vl`
```json ```json
{ {
"tool": { "tool": {
"vision": { "vision": {
"model": "ernie-4.5-turbo-vl-preview" "model": "ernie-4.5-turbo-vl"
} }
} }
} }

View File

@@ -19,7 +19,7 @@ Vision 工具采用多级自动选择 + 自动兜底策略,无需手动配置
| 厂商 | 视觉模型 | 说明 | | 厂商 | 视觉模型 | 说明 |
| --- | --- | --- | | --- | --- | --- |
| OpenAI / 兼容协议 | 使用主模型 | 支持所有 OpenAI 协议兼容的多模态模型 | | OpenAI / 兼容协议 | 使用主模型 | 支持所有 OpenAI 协议兼容的多模态模型 |
| 百度千帆 (Qianfan) | ernie-4.5-turbo-vl-preview | 配置 `qianfan_api_key` 后自动发现,也可通过 `tool.vision.model` 指定 | | 百度千帆 (Qianfan) | ernie-4.5-turbo-vl | 配置 `qianfan_api_key` 后自动发现,也可通过 `tool.vision.model` 指定 |
| 通义千问 (DashScope) | 使用主模型 | 例如 qwen3.6-plus 等 | | 通义千问 (DashScope) | 使用主模型 | 例如 qwen3.6-plus 等 |
| Claude | 使用主模型 | Anthropic 原生图像格式 | | Claude | 使用主模型 | Anthropic 原生图像格式 |
| Gemini | 使用主模型 | inlineData 格式 | | Gemini | 使用主模型 | inlineData 格式 |
@@ -42,7 +42,7 @@ Vision 工具采用多级自动选择 + 自动兜底策略,无需手动配置
{ {
"tool": { "tool": {
"vision": { "vision": {
"model": "ernie-4.5-turbo-vl-preview" "model": "ernie-4.5-turbo-vl"
} }
} }
} }

View File

@@ -15,7 +15,7 @@ from .qianfan_session import QianfanSession
DEFAULT_API_BASE = "https://qianfan.baidubce.com/v2" DEFAULT_API_BASE = "https://qianfan.baidubce.com/v2"
DEFAULT_MODEL = const.ERNIE_5 DEFAULT_MODEL = const.ERNIE_5
DEFAULT_VISION_MODEL = const.ERNIE_45_TURBO_VL_PREVIEW DEFAULT_VISION_MODEL = const.ERNIE_45_TURBO_VL
class QianfanBot(Bot, OpenAICompatibleBot): class QianfanBot(Bot, OpenAICompatibleBot):

View File

@@ -20,19 +20,19 @@ class TestQianfanConstantsAndRouting(unittest.TestCase):
self.assertEqual(const.ERNIE_45_TURBO_32K, "ernie-4.5-turbo-32k") self.assertEqual(const.ERNIE_45_TURBO_32K, "ernie-4.5-turbo-32k")
self.assertEqual(const.ERNIE_X1_TURBO_32K, "ernie-x1-turbo-32k") self.assertEqual(const.ERNIE_X1_TURBO_32K, "ernie-x1-turbo-32k")
self.assertEqual( self.assertEqual(
const.ERNIE_45_TURBO_VL_PREVIEW, const.ERNIE_45_TURBO_VL,
"ernie-4.5-turbo-vl-preview", "ernie-4.5-turbo-vl",
) )
self.assertEqual( self.assertEqual(
const.ERNIE_45_VL_28B_A3B, const.ERNIE_45_TURBO_VL_32K,
"ernie-4.5-vl-28b-a3b", "ernie-4.5-turbo-vl-32k",
) )
self.assertIn(const.QIANFAN, const.MODEL_LIST) self.assertIn(const.QIANFAN, const.MODEL_LIST)
self.assertIn(const.ERNIE_45_TURBO_128K, const.MODEL_LIST) self.assertIn(const.ERNIE_45_TURBO_128K, const.MODEL_LIST)
self.assertIn(const.ERNIE_45_TURBO_32K, const.MODEL_LIST) self.assertIn(const.ERNIE_45_TURBO_32K, const.MODEL_LIST)
self.assertIn(const.ERNIE_X1_TURBO_32K, const.MODEL_LIST) self.assertIn(const.ERNIE_X1_TURBO_32K, const.MODEL_LIST)
self.assertIn(const.ERNIE_45_TURBO_VL_PREVIEW, const.MODEL_LIST) self.assertIn(const.ERNIE_45_TURBO_VL, const.MODEL_LIST)
self.assertIn(const.ERNIE_45_VL_28B_A3B, const.MODEL_LIST) self.assertIn(const.ERNIE_45_TURBO_VL_32K, const.MODEL_LIST)
def test_qianfan_config_keys_are_available(self): def test_qianfan_config_keys_are_available(self):
import config import config
@@ -239,7 +239,7 @@ class TestQianfanBot(unittest.TestCase):
fake_response.status_code = 200 fake_response.status_code = 200
fake_response.json.return_value = { fake_response.json.return_value = {
"id": "chatcmpl-test", "id": "chatcmpl-test",
"model": "ernie-4.5-turbo-vl-preview", "model": "ernie-4.5-turbo-vl",
"choices": [{"message": {"content": "图中有一个红色方块。"}}], "choices": [{"message": {"content": "图中有一个红色方块。"}}],
"usage": { "usage": {
"prompt_tokens": 10, "prompt_tokens": 10,
@@ -260,14 +260,14 @@ class TestQianfanBot(unittest.TestCase):
) )
self.assertEqual(result["content"], "图中有一个红色方块。") self.assertEqual(result["content"], "图中有一个红色方块。")
self.assertEqual(result["model"], "ernie-4.5-turbo-vl-preview") self.assertEqual(result["model"], "ernie-4.5-turbo-vl")
self.assertEqual(result["usage"]["total_tokens"], 18) self.assertEqual(result["usage"]["total_tokens"], 18)
post.assert_called_once() post.assert_called_once()
url = post.call_args.args[0] url = post.call_args.args[0]
kwargs = post.call_args.kwargs kwargs = post.call_args.kwargs
self.assertEqual(url, "https://qianfan.baidubce.com/v2/chat/completions") self.assertEqual(url, "https://qianfan.baidubce.com/v2/chat/completions")
self.assertEqual(kwargs["headers"]["Authorization"], "Bearer test-qianfan-key") self.assertEqual(kwargs["headers"]["Authorization"], "Bearer test-qianfan-key")
self.assertEqual(kwargs["json"]["model"], "ernie-4.5-turbo-vl-preview") self.assertEqual(kwargs["json"]["model"], "ernie-4.5-turbo-vl")
self.assertEqual(kwargs["json"]["max_tokens"], 1000) self.assertEqual(kwargs["json"]["max_tokens"], 1000)
self.assertEqual(kwargs["json"]["messages"], [ self.assertEqual(kwargs["json"]["messages"], [
{ {
@@ -287,7 +287,7 @@ class TestQianfanBot(unittest.TestCase):
fake_response = MagicMock() fake_response = MagicMock()
fake_response.status_code = 200 fake_response.status_code = 200
fake_response.json.return_value = { fake_response.json.return_value = {
"model": "ernie-4.5-vl-28b-a3b", "model": "ernie-4.5-turbo-vl-32k",
"choices": [{"message": {"content": "有文字。"}}], "choices": [{"message": {"content": "有文字。"}}],
"usage": {}, "usage": {},
} }
@@ -301,12 +301,12 @@ class TestQianfanBot(unittest.TestCase):
result = bot.call_vision( result = bot.call_vision(
image_url="data:image/jpeg;base64,BBBB", image_url="data:image/jpeg;base64,BBBB",
question="识别文字", question="识别文字",
model="ernie-4.5-vl-28b-a3b", model="ernie-4.5-turbo-vl-32k",
max_tokens=256, max_tokens=256,
) )
self.assertEqual(result["model"], "ernie-4.5-vl-28b-a3b") self.assertEqual(result["model"], "ernie-4.5-turbo-vl-32k")
self.assertEqual(post.call_args.kwargs["json"]["model"], "ernie-4.5-vl-28b-a3b") self.assertEqual(post.call_args.kwargs["json"]["model"], "ernie-4.5-turbo-vl-32k")
self.assertEqual(post.call_args.kwargs["json"]["max_tokens"], 256) self.assertEqual(post.call_args.kwargs["json"]["max_tokens"], 256)
def test_call_vision_returns_error_dict_for_api_error(self): def test_call_vision_returns_error_dict_for_api_error(self):
@@ -392,14 +392,14 @@ class TestQianfanVisionTool(unittest.TestCase):
providers = tool._resolve_providers() providers = tool._resolve_providers()
self.assertEqual(providers[0].name, "Qianfan") self.assertEqual(providers[0].name, "Qianfan")
self.assertEqual(providers[0].model_override, const.ERNIE_45_TURBO_VL_PREVIEW) self.assertEqual(providers[0].model_override, const.ERNIE_45_TURBO_VL)
self.assertTrue(providers[0].use_bot) self.assertTrue(providers[0].use_bot)
create_bot.assert_called_with(const.QIANFAN) create_bot.assert_called_with(const.QIANFAN)
def test_vision_routes_ernie_model_override_to_qianfan(self): def test_vision_routes_ernie_model_override_to_qianfan(self):
fake_conf = self._fake_conf({ fake_conf = self._fake_conf({
"qianfan_api_key": "test-qianfan-key", "qianfan_api_key": "test-qianfan-key",
"tool": {"vision": {"model": "ernie-4.5-vl-28b-a3b"}}, "tool": {"vision": {"model": "ernie-4.5-turbo-vl-32k"}},
}) })
fake_bot = MagicMock() fake_bot = MagicMock()
fake_bot.call_vision = MagicMock() fake_bot.call_vision = MagicMock()
@@ -413,10 +413,10 @@ class TestQianfanVisionTool(unittest.TestCase):
providers = tool._resolve_providers() providers = tool._resolve_providers()
self.assertEqual(providers[0].name, "Qianfan") self.assertEqual(providers[0].name, "Qianfan")
self.assertEqual(providers[0].model_override, "ernie-4.5-vl-28b-a3b") self.assertEqual(providers[0].model_override, "ernie-4.5-turbo-vl-32k")
def test_vision_main_model_uses_qianfan_when_configured_model_is_ernie(self): def test_vision_main_model_uses_qianfan_when_configured_model_is_ernie(self):
fake_conf = self._fake_conf({"model": "ernie-4.5-vl-28b-a3b"}) fake_conf = self._fake_conf({"model": "ernie-4.5-turbo-vl-32k"})
from common import const from common import const
fake_model = MagicMock() fake_model = MagicMock()
@@ -433,7 +433,7 @@ class TestQianfanVisionTool(unittest.TestCase):
providers = tool._resolve_providers() providers = tool._resolve_providers()
self.assertEqual(providers[0].name, "MainModel") self.assertEqual(providers[0].name, "MainModel")
self.assertEqual(providers[0].model_override, "ernie-4.5-vl-28b-a3b") self.assertEqual(providers[0].model_override, "ernie-4.5-turbo-vl-32k")
class TestQianfanDocs(unittest.TestCase): class TestQianfanDocs(unittest.TestCase):
@@ -452,7 +452,7 @@ class TestQianfanDocs(unittest.TestCase):
self.assertIn("qianfan_api_key", text) self.assertIn("qianfan_api_key", text)
self.assertIn("https://qianfan.baidubce.com/v2", text) self.assertIn("https://qianfan.baidubce.com/v2", text)
self.assertIn("ernie-4.5-turbo-128k", text) self.assertIn("ernie-4.5-turbo-128k", text)
self.assertIn("ernie-4.5-turbo-vl-preview", text) self.assertIn("ernie-4.5-turbo-vl", text)
def test_model_indexes_link_qianfan(self): def test_model_indexes_link_qianfan(self):
for path in ( for path in (
@@ -479,7 +479,7 @@ class TestQianfanDocs(unittest.TestCase):
for path, label in expected.items(): for path, label in expected.items():
text = self._read(path) text = self._read(path)
self.assertIn(label, text) self.assertIn(label, text)
self.assertIn("ernie-4.5-turbo-vl-preview", text) self.assertIn("ernie-4.5-turbo-vl", text)
if __name__ == "__main__": if __name__ == "__main__":