mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-09-03 01:31:47 +03:00
feat(providers): support DeepSeek V4 vision
This commit is contained in:
@@ -368,6 +368,46 @@ async def test_deepseek_v4_pro_uses_responses_api() -> None:
|
||||
assert "include" not in call_kwargs
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_deepseek_vision_uses_responses_api_with_image_input() -> None:
|
||||
mock_chat = AsyncMock(return_value=_fake_chat_response())
|
||||
mock_responses = AsyncMock(return_value=_fake_responses_response("vision response"))
|
||||
content = [
|
||||
{"type": "text", "text": "describe this image"},
|
||||
{"type": "image_url", "image_url": {"url": "data:image/png;base64,AA=="}},
|
||||
]
|
||||
|
||||
with patch("nanobot.providers.openai_compat_provider.AsyncOpenAI") as mock_client_class:
|
||||
client_instance = mock_client_class.return_value
|
||||
client_instance.chat.completions.create = mock_chat
|
||||
client_instance.responses.create = mock_responses
|
||||
|
||||
provider = OpenAICompatProvider(
|
||||
api_key="sk-test-key",
|
||||
default_model="deepseek-v4-flash-vision-exp",
|
||||
spec=find_by_name("deepseek"),
|
||||
)
|
||||
result = await provider.chat(
|
||||
messages=[{"role": "user", "content": content}],
|
||||
model="deepseek-v4-flash-vision-exp",
|
||||
)
|
||||
|
||||
assert result.content == "vision response"
|
||||
mock_chat.assert_not_awaited()
|
||||
call_kwargs = mock_responses.call_args.kwargs
|
||||
assert call_kwargs["input"] == [{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "input_text", "text": "describe this image"},
|
||||
{
|
||||
"type": "input_image",
|
||||
"image_url": "data:image/png;base64,AA==",
|
||||
"detail": "auto",
|
||||
},
|
||||
],
|
||||
}]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize(
|
||||
("provider_name", "model"),
|
||||
@@ -1557,6 +1597,33 @@ def test_deepseek_coerces_list_content_to_string() -> None:
|
||||
assert "world" in kw["messages"][0]["content"]
|
||||
|
||||
|
||||
def test_deepseek_vision_preserves_multimodal_content() -> None:
|
||||
"""DeepSeek's vision model requires OpenAI-compatible content blocks."""
|
||||
spec = find_by_name("deepseek")
|
||||
with patch("nanobot.providers.openai_compat_provider.AsyncOpenAI"):
|
||||
p = OpenAICompatProvider(
|
||||
api_key="k",
|
||||
default_model="deepseek-v4-flash-vision-exp",
|
||||
spec=spec,
|
||||
)
|
||||
content = [
|
||||
{"type": "text", "text": "describe this image"},
|
||||
{"type": "image_url", "image_url": {"url": "data:image/png;base64,AA=="}},
|
||||
]
|
||||
|
||||
kw = p._build_kwargs(
|
||||
messages=[{"role": "user", "content": content}],
|
||||
tools=None,
|
||||
model="deepseek-v4-flash-vision-exp",
|
||||
max_tokens=1024,
|
||||
temperature=0.7,
|
||||
reasoning_effort=None,
|
||||
tool_choice=None,
|
||||
)
|
||||
|
||||
assert kw["messages"][0]["content"] == content
|
||||
|
||||
|
||||
def test_non_deepseek_keeps_list_content() -> None:
|
||||
"""Only DeepSeek should force string content; OpenAI-compatible providers keep blocks."""
|
||||
spec = find_by_name("openai")
|
||||
|
||||
Reference in New Issue
Block a user