File size: 3,018 Bytes
fca155a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import pytest
from unittest.mock import MagicMock, patch, ANY
from pathlib import Path
import sys

# 1. Mock the dependencies BEFORE import
mock_llama = MagicMock()
mock_chat_handler = MagicMock()
sys.modules["llama_cpp"] = mock_llama
sys.modules["llama_cpp.llama_chat_format"] = MagicMock()
sys.modules["llama_cpp.llama_chat_format"].Llava15ChatHandler = mock_chat_handler

from src.perception.engine import Qwen2PerceptionEngine
from src.config.settings import settings

class TestPerceptionEngine:
    
    @patch("src.perception.engine.settings")
    def test_load_model_calls_llama_correctly(self, mock_settings):
        """Test that load_model initializes the Llama class with GPU settings."""
        # Setup
        engine = Qwen2PerceptionEngine()
        fake_path = Path("/tmp/fake_model.gguf")
        fake_projector = Path("/tmp/mmproj.gguf")
        
        # Mock the glob search for the projector
        mock_settings.paths.models_dir.glob.return_value = [fake_projector]
        
        # Act
        engine.load_model(fake_path)
        
        # Assert
        # 1. Check if it looked for the projector
        mock_settings.paths.models_dir.glob.assert_called()
        
        # 2. Check if ChatHandler was initialized
        mock_chat_handler.assert_called_with(clip_model_path=str(fake_projector))
        
        # 3. Check if Llama was instantiated with GPU layers
        mock_llama.Llama.assert_called_with(
            model_path=str(fake_path),
            chat_handler=ANY, # The instance of chat handler
            n_ctx=2048,
            n_gpu_layers=-1, # Important: Must be -1 for full GPU
            n_batch=512,
            verbose=False
        )

    def test_analyze_frame_structure(self):
        """Test that analyze_frame constructs the correct message format for Qwen."""
        engine = Qwen2PerceptionEngine()
        
        # Mock the internal Llama model
        engine._model = MagicMock()
        engine._model.create_chat_completion.return_value = {
            "choices": [{"message": {"content": "A dog on a bike"}}]
        }
        
        # Mock image loader to avoid file IO
        with patch("src.perception.engine.open", create=True) as mock_open:
            mock_open.return_value.__enter__.return_value.read.return_value = b"fake_image_bytes"
            
            result = engine.analyze_frame("test.jpg", "Describe this")
            
            # Assert
            assert result == "A dog on a bike"
            
            # Verify the prompt structure
            calls = engine._model.create_chat_completion.call_args
            messages = calls.kwargs['messages']
            
            assert messages[0]['role'] == 'system'
            assert messages[1]['role'] == 'user'
            # Check content list (Image + Text)
            content = messages[1]['content']
            assert content[0]['type'] == 'image_url'
            assert content[1]['type'] == 'text'
            assert content[1]['text'] == 'Describe this'