File size: 17,755 Bytes
76f9cd2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
"""
Test Modal Final Improvements - Updated for new service architecture
Tests model preloading, distributed processing with enhanced segmentation, and speaker diarization
"""

import asyncio
import pytest
import os
import time
from pathlib import Path

# Import from new service architecture
from src.services import (
    ModalTranscriptionService, 
    ModalDownloadService, 
    HealthService,
    TranscriptionService,
    DistributedTranscriptionService
)

# Import updated tools
from src.tools.transcription_tools import (
    transcribe_audio_file_tool,
    check_modal_endpoints_health,
    get_system_status
)

from src.tools.download_tools import (
    get_file_info_tool,
    read_text_file_segments_tool
)


class TestModalFinalImprovements:
    """Test suite for Modal improvements with new architecture"""
    
    @pytest.mark.asyncio
    async def test_model_preloading_health_check(self):
        """Test that models are properly preloaded in Modal"""
        print("\nπŸ—οΈ Testing model preloading health check...")
        
        health_status = await check_modal_endpoints_health()
        
        # Check if health check endpoint responded
        assert "health_check" in health_status, "Health check endpoint not found"
        health_endpoint = health_status["health_check"]
        
        if health_endpoint["status"] == "healthy":
            print("βœ… Health check endpoint is accessible")
            
            # Get detailed system status
            system_status = await get_system_status()
            
            # Check Whisper status
            whisper_status = system_status.get("whisper", {})
            print(f"πŸ€– Whisper status: {whisper_status.get('status', 'unknown')}")
            print(f"🎯 Default model: {whisper_status.get('default_model', 'unknown')}")
            print(f"πŸ“¦ Model cache exists: {whisper_status.get('model_cache_exists', False)}")
            
            # Verify turbo model is available
            available_models = whisper_status.get("available_models", [])
            assert "turbo" in available_models, f"Turbo model not available. Available: {available_models}"
            
            # Check speaker diarization status
            speaker_status = system_status.get("speaker_diarization", {})
            print(f"πŸ‘₯ Speaker diarization: {speaker_status.get('status', 'unknown')}")
            print(f"πŸ”‘ HF Token available: {speaker_status.get('hf_token_available', False)}")
            
        else:
            print(f"⚠️ Health check endpoint not healthy: {health_endpoint.get('error', 'Unknown error')}")
            pytest.skip("Health check endpoint not accessible")

    @pytest.mark.asyncio
    async def test_distributed_processing_with_turbo_model(self):
        """Test distributed processing using turbo model"""
        print("\nπŸ”„ Testing distributed processing with turbo model...")
        
        # Check if we have test audio files
        test_audio_files = [
            "tests/cache/apple_podcast_episode.mp3",
            "tests/cache/xyz_podcast_episode.mp3"
        ]
        
        available_files = [f for f in test_audio_files if os.path.exists(f)]
        
        if not available_files:
            pytest.skip("No test audio files available. Run real-world integration tests first.")
        
        # Use the larger file for better distributed processing test
        test_file = max(available_files, key=lambda f: os.path.getsize(f))
        file_size_mb = os.path.getsize(test_file) / (1024 * 1024)
        
        print(f"πŸ“ Using test file: {test_file} ({file_size_mb:.2f} MB)")
        
        start_time = time.time()
        
        # Test distributed processing with turbo model
        result = await transcribe_audio_file_tool(
            audio_file_path=test_file,
            model_size="turbo",  # Explicitly use turbo model
            language=None,  # Auto-detect
            output_format="srt",
            enable_speaker_diarization=False,  # Test without speaker diarization first
            use_parallel_processing=True,  # Force distributed processing
            chunk_duration=60,  # 60 seconds chunks
            use_intelligent_segmentation=True  # Use intelligent segmentation
        )
        
        end_time = time.time()
        processing_time = end_time - start_time
        
        # Verify transcription succeeded
        assert result["processing_status"] == "success", \
            f"Distributed transcription failed: {result.get('error_message', 'Unknown error')}"
        
        # Check that distributed processing was used
        distributed_processing = result.get("distributed_processing", False)
        chunks_processed = result.get("chunks_processed", 0)
        chunks_failed = result.get("chunks_failed", 0)
        segmentation_type = result.get("segmentation_type", "unknown")
        
        print(f"πŸ“Š Distributed processing results:")
        print(f"   Processing time: {processing_time:.2f}s")
        print(f"   Model used: {result.get('model_used', 'unknown')}")
        print(f"   Segments: {result.get('segment_count', 0)}")
        print(f"   Duration: {result.get('audio_duration', 0):.2f}s")
        print(f"   Language: {result.get('language_detected', 'unknown')}")
        print(f"   Distributed processing: {distributed_processing}")
        print(f"   Chunks processed: {chunks_processed}")
        print(f"   Chunks failed: {chunks_failed}")
        print(f"   Segmentation type: {segmentation_type}")
        
        # Verify that distributed processing was used for large files
        if result.get("audio_duration", 0) > 120:  # Files longer than 2 minutes
            assert distributed_processing, "Distributed processing should be used for long audio files"
            assert chunks_processed > 1, f"Expected multiple chunks, got {chunks_processed}"
        
        # Verify turbo model was used
        assert result.get("model_used") == "turbo", \
            f"Expected turbo model, got {result.get('model_used')}"
        
        # Note: Output files are created on Modal server, not locally
        # Verify transcription content instead
        assert result.get("segment_count", 0) > 0, "No transcription segments found"
        assert result.get("audio_duration", 0) > 0, "No audio duration detected"

    def test_health_check_with_model_preloading(self):
        """Test health service functionality"""
        print("\nπŸ” Testing health service with model preloading...")
        
        health_service = HealthService()
        
        # Test Whisper models check
        whisper_status = health_service._check_whisper_models()
        print(f"πŸ€– Whisper status: {whisper_status}")
        
        assert whisper_status["default_model"] == "turbo"
        assert "turbo" in whisper_status["available_models"]
        
        # Test speaker diarization check
        speaker_status = health_service._check_speaker_diarization()
        print(f"πŸ‘₯ Speaker status: {speaker_status}")
        
        # Status can be healthy, partial, or disabled
        assert speaker_status["status"] in ["healthy", "partial", "disabled"]

    def test_speaker_diarization_pipeline_loading(self):
        """Test speaker diarization pipeline loading"""
        print("\nπŸ‘₯ Testing speaker diarization pipeline...")
        
        transcription_service = TranscriptionService()
        
        # Test loading speaker diarization pipeline
        pipeline = transcription_service._load_speaker_diarization_pipeline()
        
        if pipeline is not None:
            print("βœ… Speaker diarization pipeline loaded successfully")
            # Test with actual pipeline
            assert hasattr(pipeline, '__call__'), "Pipeline should be callable"
        else:
            print("⚠️ Speaker diarization pipeline not available (likely missing HF_TOKEN)")
            # This is acceptable if HF_TOKEN is not configured

    @pytest.mark.asyncio
    async def test_transcription_service_with_speaker_diarization(self):
        """Test local transcription service with speaker diarization"""
        print("\n🎀 Testing transcription service with speaker diarization...")
        
        # Check if we have test audio files
        test_audio_files = [
            "tests/cache/apple_podcast_episode.mp3",
            "tests/cache/xyz_podcast_episode.mp3"
        ]
        
        available_files = [f for f in test_audio_files if os.path.exists(f)]
        
        if not available_files:
            pytest.skip("No test audio files available")
        
        # Use smaller file for local processing
        test_file = min(available_files, key=lambda f: os.path.getsize(f))
        
        transcription_service = TranscriptionService()
        
        # Test transcription with speaker diarization enabled
        result = transcription_service.transcribe_audio(
            audio_file_path=test_file,
            model_size="turbo",
            enable_speaker_diarization=True
        )
        
        assert result["processing_status"] == "success"
        assert result["model_used"] == "turbo"
        
        # Check speaker diarization results
        speaker_enabled = result.get("speaker_diarization_enabled", False)
        speaker_count = result.get("global_speaker_count", 0)
        
        print(f"πŸ‘₯ Speaker diarization enabled: {speaker_enabled}")
        print(f"πŸ‘₯ Speakers detected: {speaker_count}")
        
        if speaker_enabled:
            print("βœ… Speaker diarization worked successfully")
        else:
            print("⚠️ Speaker diarization was disabled (likely missing dependencies)")

    @pytest.mark.asyncio 
    async def test_speaker_diarization_with_real_audio(self):
        """Test speaker diarization with real audio file"""
        print("\n🎯 Testing speaker diarization with real audio...")
        
        # Check if we have audio files available
        test_audio_files = [
            "tests/cache/apple_podcast_episode.mp3",
            "tests/cache/xyz_podcast_episode.mp3"
        ]
        
        available_files = [f for f in test_audio_files if os.path.exists(f)]
        
        if not available_files:
            pytest.skip("No test audio files available")
        
        test_file = available_files[0]  # Use first available file
        
        # Test with TranscriptionService
        transcription_service = TranscriptionService()
        
        result = transcription_service.transcribe_audio(
            audio_file_path=test_file,
            model_size="turbo", 
            enable_speaker_diarization=True
        )
        
        assert result["processing_status"] == "success"
        
        # Check speaker information
        speakers_detected = result.get("global_speaker_count", 0)
        speaker_enabled = result.get("speaker_diarization_enabled", False)
        
        print(f"🎯 Speaker diarization results:")
        print(f"   Enabled: {speaker_enabled}")
        print(f"   Speakers detected: {speakers_detected}")
        print(f"   Audio duration: {result.get('audio_duration', 0):.2f}s")
        print(f"   Segments: {result.get('segment_count', 0)}")

    @pytest.mark.asyncio
    async def test_distributed_transcription_with_speaker_diarization(self):
        """Test distributed transcription with speaker diarization"""
        print("\n🎯 Testing distributed transcription with speaker diarization...")
        
        # This test focuses on the distributed service architecture
        distributed_service = DistributedTranscriptionService()
        
        # Test segmentation strategies with non-existent file
        test_file = "dummy_audio.mp3"  # Dummy file for testing
        
        # Test intelligent segmentation choice - should handle missing files gracefully
        try:
            segments = distributed_service.choose_segmentation_strategy(test_file)
            # If no exception is raised, the service handled it gracefully
            print("βœ… Distributed service properly handles missing files without exceptions")
        except Exception as e:
            # This is also acceptable - service detected the missing file
            print(f"βœ… Distributed service properly detected missing file: {type(e).__name__}")
        
        # Test with actual audio file if available
        test_audio_files = [
            "tests/cache/apple_podcast_episode.mp3",
            "tests/cache/xyz_podcast_episode.mp3"
        ]
        
        available_files = [f for f in test_audio_files if os.path.exists(f)]
        
        if available_files:
            test_file = available_files[0]
            try:
                segments = distributed_service.choose_segmentation_strategy(test_file)
                print(f"βœ… Segmentation strategy worked for real file: {segments}")
            except Exception as e:
                print(f"⚠️ Segmentation strategy failed: {e}")
        else:
            print("⚠️ No test audio files available for segmentation testing")

    def test_local_startup_with_new_architecture(self):
        """Test that all imports work correctly in new architecture"""
        print("\nπŸš€ Testing local startup with new architecture...")
        
        # Test core service imports
        try:
            from src.services.transcription_service import TranscriptionService
            print("βœ… TranscriptionService imported successfully")
        except ImportError as e:
            pytest.fail(f"Failed to import TranscriptionService: {e}")
        
        try:
            from src.services.distributed_transcription_service import DistributedTranscriptionService
            print("βœ… DistributedTranscriptionService imported successfully")
        except ImportError as e:
            pytest.fail(f"Failed to import DistributedTranscriptionService: {e}")
        
        try:
            from src.services.health_service import HealthService
            print("βœ… HealthService imported successfully")
        except ImportError as e:
            pytest.fail(f"Failed to import HealthService: {e}")
        
        # Test Modal services
        try:
            from src.services.modal_transcription_service import ModalTranscriptionService
            # Note: ModalDownloadService removed - downloads now handled locally
            print("βœ… Modal services imported successfully")
        except ImportError as e:
            pytest.fail(f"Failed to import Modal services: {e}")
        
        # Test tools imports
        try:
            from src.tools.transcription_tools import (
                transcribe_audio_file_tool,
                check_modal_endpoints_health
            )
            print("βœ… Transcription tools imported successfully")
        except ImportError as e:
            pytest.fail(f"Failed to import transcription tools: {e}")
        
        try:
            from src.tools.download_tools import (
                get_file_info_tool,
                read_text_file_segments_tool
            )
            print("βœ… Download tools imported successfully")
        except ImportError as e:
            pytest.fail(f"Failed to import download tools: {e}")
        
        # Test service registry
        try:
            from src.services import get_service, list_available_services
            
            # Test getting services
            transcription_service = get_service("transcription")
            assert transcription_service is not None
            
            modal_service = get_service("modal_transcription")
            assert modal_service is not None
            
            # Test service listing
            available_services = list_available_services()
            assert "transcription" in available_services
            assert "modal_transcription" in available_services
            
            print("βœ… Service registry working correctly")
        except Exception as e:
            pytest.fail(f"Service registry error: {e}")

    @pytest.mark.asyncio
    async def test_modal_endpoints_availability(self):
        """Test Modal endpoints availability"""
        print("\n🌐 Testing Modal endpoints availability...")
        
        modal_service = ModalTranscriptionService()
        
        health_status = await modal_service.check_endpoints_health()
        
        print(f"πŸ” Endpoint health status:")
        for endpoint_name, status in health_status.items():
            print(f"   {endpoint_name}: {status.get('status', 'unknown')}")
        
        # At least health check should be accessible
        health_check_status = health_status.get("health_check", {})
        if health_check_status.get("status") == "healthy":
            print("βœ… Health check endpoint is working")
        else:
            print("⚠️ Health check endpoint may not be available")

    def test_model_cache_usage(self):
        """Test model cache usage in transcription service"""
        print("\nπŸ“¦ Testing model cache usage...")
        
        transcription_service = TranscriptionService()
        
        # Test model loading (should use cache if available)
        model = transcription_service._load_cached_model("turbo")
        assert model is not None
        
        print("βœ… Model loading successful")
        
        # Test speaker diarization pipeline loading
        pipeline = transcription_service._load_speaker_diarization_pipeline()
        
        if pipeline is not None:
            print("βœ… Speaker diarization pipeline loaded")
        else:
            print("⚠️ Speaker diarization pipeline not available")


if __name__ == "__main__":
    pytest.main([__file__, "-v"])