mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-09-30 12:43:16 +00:00
93 lines
3.3 KiB
Python
93 lines
3.3 KiB
Python
|
|
import base64
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
from fastapi import HTTPException
|
||
|
|
|
||
|
|
from agent.dashboard import thread_api
|
||
|
|
from agent.dashboard.options import model_supports_images
|
||
|
|
|
||
|
|
_TEXT_ONLY_MODEL = "fireworks:accounts/fireworks/models/deepseek-v4-pro"
|
||
|
|
_VISION_MODEL = "openai:gpt-5.5"
|
||
|
|
|
||
|
|
|
||
|
|
def _image() -> thread_api.DashboardImageBody:
|
||
|
|
return thread_api.DashboardImageBody(
|
||
|
|
base64=base64.b64encode(b"image").decode("ascii"),
|
||
|
|
mimeType="image/png",
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def test_model_supports_images_marks_text_only_fireworks_models() -> None:
|
||
|
|
assert not model_supports_images(_TEXT_ONLY_MODEL)
|
||
|
|
assert model_supports_images(_VISION_MODEL)
|
||
|
|
|
||
|
|
|
||
|
|
def test_user_message_content_rejects_images_for_text_only_model() -> None:
|
||
|
|
with pytest.raises(HTTPException) as exc_info:
|
||
|
|
thread_api._user_message_content("see attached", [_image()], model_id=_TEXT_ONLY_MODEL)
|
||
|
|
|
||
|
|
assert exc_info.value.status_code == 422
|
||
|
|
assert "does not support image input" in exc_info.value.detail
|
||
|
|
|
||
|
|
|
||
|
|
def test_user_message_content_allows_images_for_vision_model() -> None:
|
||
|
|
content = thread_api._user_message_content("see attached", [_image()], model_id=_VISION_MODEL)
|
||
|
|
|
||
|
|
assert isinstance(content, list)
|
||
|
|
assert content[-1] == {"type": "text", "text": "see attached"}
|
||
|
|
assert any(block.get("type") != "text" for block in content)
|
||
|
|
|
||
|
|
|
||
|
|
async def test_resolve_agent_model_choice_applies_profile_before_team_default(monkeypatch) -> None:
|
||
|
|
async def fake_team_default(role: str) -> tuple[str, str]:
|
||
|
|
assert role == "agent"
|
||
|
|
return _VISION_MODEL, "medium"
|
||
|
|
|
||
|
|
monkeypatch.setattr(thread_api, "get_team_default_model", fake_team_default)
|
||
|
|
|
||
|
|
model_id, effort = await thread_api._resolve_agent_model_choice(
|
||
|
|
{"default_model": _TEXT_ONLY_MODEL, "reasoning_effort": "high"},
|
||
|
|
None,
|
||
|
|
None,
|
||
|
|
)
|
||
|
|
|
||
|
|
assert (model_id, effort) == (_TEXT_ONLY_MODEL, "high")
|
||
|
|
|
||
|
|
|
||
|
|
async def test_resolve_agent_model_choice_applies_request_before_profile(monkeypatch) -> None:
|
||
|
|
async def fake_team_default(role: str) -> tuple[str, str]:
|
||
|
|
assert role == "agent"
|
||
|
|
return _VISION_MODEL, "medium"
|
||
|
|
|
||
|
|
monkeypatch.setattr(thread_api, "get_team_default_model", fake_team_default)
|
||
|
|
|
||
|
|
model_id, effort = await thread_api._resolve_agent_model_choice(
|
||
|
|
{"default_model": _TEXT_ONLY_MODEL, "reasoning_effort": "high"},
|
||
|
|
"anthropic:claude-opus-4-8",
|
||
|
|
"high",
|
||
|
|
)
|
||
|
|
|
||
|
|
assert (model_id, effort) == ("anthropic:claude-opus-4-8", "high")
|
||
|
|
|
||
|
|
|
||
|
|
async def test_create_dashboard_thread_rejects_images_for_resolved_text_only_model(
|
||
|
|
monkeypatch,
|
||
|
|
) -> None:
|
||
|
|
async def fake_profile(login: str) -> dict[str, str]:
|
||
|
|
assert login == "octocat"
|
||
|
|
return {"default_model": _TEXT_ONLY_MODEL, "reasoning_effort": "high"}
|
||
|
|
|
||
|
|
async def fake_team_default(role: str) -> tuple[str, str]:
|
||
|
|
assert role == "agent"
|
||
|
|
return _VISION_MODEL, "medium"
|
||
|
|
|
||
|
|
monkeypatch.setattr(thread_api, "get_profile", fake_profile)
|
||
|
|
monkeypatch.setattr(thread_api, "get_team_default_model", fake_team_default)
|
||
|
|
|
||
|
|
body = thread_api.ThreadCreateBody(prompt="see attached", images=[_image()])
|
||
|
|
with pytest.raises(HTTPException) as exc_info:
|
||
|
|
await thread_api.create_dashboard_thread("octocat", body)
|
||
|
|
|
||
|
|
assert exc_info.value.status_code == 422
|
||
|
|
assert "does not support image input" in exc_info.value.detail
|