156 lines
5.7 KiB
Python
156 lines
5.7 KiB
Python
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from photools3.llm_session import LlmSession
|
|
|
|
|
|
def test_llm_session_mocked(test_photo):
|
|
# We want to mock OpenAI client's chat.completions.create
|
|
with patch("photools3.llm_session.OpenAI") as mocked_openai:
|
|
# Setup the mock response structure
|
|
mock_client = MagicMock()
|
|
mocked_openai.return_value = mock_client
|
|
|
|
# Mock the models list to return a proper object with an ID string
|
|
# This prevents Pydantic ValidationError when LlmSession tries to use model_id
|
|
mock_model = MagicMock()
|
|
mock_model.id = "gpt-4o"
|
|
mock_client.models.list.return_value.data = [mock_model]
|
|
|
|
# Create a response object: response.choices[0].message.content
|
|
mock_response = MagicMock()
|
|
mock_choice = MagicMock()
|
|
mock_message = MagicMock()
|
|
|
|
mock_message.content = '{"text": "some text"}'
|
|
mock_choice.message = mock_message
|
|
mock_response.choices = [mock_choice]
|
|
|
|
# Configure the client to return our mock response
|
|
mock_client.chat.completions.create.return_value = mock_response
|
|
|
|
# Create a new LlmSession instance that will use the mocked OpenAI client
|
|
mocked_session = LlmSession(system_prompt="You are a helper.")
|
|
|
|
# Run the test
|
|
result = mocked_session.call(
|
|
prompt="keywords for the attached picture", photos=[test_photo]
|
|
)
|
|
|
|
# Verify the results
|
|
assert result.text == "some text"
|
|
|
|
# Verify that the mock was enough
|
|
assert mock_client.chat.completions.create.called
|
|
|
|
|
|
def test_llm_session_captions_and_keywords_mocked(test_photo):
|
|
# We want to mock OpenAI client's chat.completions.create
|
|
with patch("photools3.llm_session.OpenAI") as mocked_openai:
|
|
# Setup the mock response structure
|
|
mock_client = MagicMock()
|
|
mocked_openai.return_value = mock_client
|
|
|
|
# Mock the models list to return a proper object with an ID string
|
|
# This prevents Pydantic ValidationError when LlmSession tries to use model_id
|
|
mock_model = MagicMock()
|
|
mock_model.id = "gpt-4o"
|
|
mock_client.models.list.return_value.data = [mock_model]
|
|
|
|
# Create a response object: response.choices[0].message.content
|
|
mock_response = MagicMock()
|
|
mock_choice = MagicMock()
|
|
mock_message = MagicMock()
|
|
|
|
mock_message.content = (
|
|
'{"caption": "A mocked caption", "tags": ["architektur"]}'
|
|
)
|
|
mock_choice.message = mock_message
|
|
mock_response.choices = [mock_choice]
|
|
|
|
# Configure the client to return our mock response
|
|
mock_client.chat.completions.create.return_value = mock_response
|
|
|
|
# Create a new LlmSessionCaptionsAndKeywords instance that will use the mocked OpenAI client
|
|
from photools3.llm_session import LlmSessionCaptionsAndKeywords
|
|
|
|
mocked_session = LlmSessionCaptionsAndKeywords()
|
|
|
|
# Run the test
|
|
result = mocked_session.call(photos=[test_photo])
|
|
|
|
# Verify the results
|
|
assert result.caption == "A mocked caption"
|
|
assert "architektur" in result.tags
|
|
|
|
# Verify that the mock was called
|
|
assert mock_client.chat.completions.create.called
|
|
|
|
|
|
def test_llm_session_retries_from_original_request_without_accumulating_messages():
|
|
with patch("photools3.llm_session.OpenAI") as mocked_openai:
|
|
mock_client = MagicMock()
|
|
mocked_openai.return_value = mock_client
|
|
mock_model = MagicMock()
|
|
mock_model.id = "gpt-4o"
|
|
mock_client.models.list.return_value.data = [mock_model]
|
|
|
|
def response(content):
|
|
message = MagicMock()
|
|
message.content = content
|
|
choice = MagicMock()
|
|
choice.message = message
|
|
result = MagicMock()
|
|
result.choices = [choice]
|
|
return result
|
|
|
|
mock_client.chat.completions.create.side_effect = [
|
|
response('{"wrong": "first"}'),
|
|
response('{"wrong": "second"}'),
|
|
response('{"text": "correct"}'),
|
|
]
|
|
|
|
session = LlmSession(system_prompt="You are a helper.")
|
|
result = session.call(photos=[], prompt="Return JSON")
|
|
|
|
assert result.text == "correct"
|
|
requests = mock_client.chat.completions.create.call_args_list
|
|
messages_per_attempt = [call.kwargs["messages"] for call in requests]
|
|
assert [len(messages) for messages in messages_per_attempt] == [2, 3, 3]
|
|
final_correction = messages_per_attempt[2][-1]["content"]
|
|
assert '"wrong": "second"' in final_correction
|
|
assert '"wrong": "first"' not in final_correction
|
|
|
|
|
|
def test_llm_session_uses_configured_retry_count(monkeypatch):
|
|
monkeypatch.setattr("photools3.llm_session.settings.llm_max_retries", 1)
|
|
|
|
with patch("photools3.llm_session.OpenAI") as mocked_openai:
|
|
mock_client = MagicMock()
|
|
mocked_openai.return_value = mock_client
|
|
mock_model = MagicMock()
|
|
mock_model.id = "gpt-4o"
|
|
mock_client.models.list.return_value.data = [mock_model]
|
|
|
|
def response(content):
|
|
message = MagicMock()
|
|
message.content = content
|
|
choice = MagicMock()
|
|
choice.message = message
|
|
result = MagicMock()
|
|
result.choices = [choice]
|
|
return result
|
|
|
|
mock_client.chat.completions.create.side_effect = [
|
|
response('{"wrong": "first"}'),
|
|
response('{"wrong": "second"}'),
|
|
response('{"text": "would be a third call"}'),
|
|
]
|
|
session = LlmSession(system_prompt="You are a helper.")
|
|
|
|
with pytest.raises(ValueError, match="after 1 retries"):
|
|
session.call(photos=[])
|
|
|
|
assert mock_client.chat.completions.create.call_count == 2
|