fix: Restore all files lost during destructive rebase
A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.
This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).
Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
195
backend/tests/test_llm_gateway/test_inference_service.py
Normal file
195
backend/tests/test_llm_gateway/test_inference_service.py
Normal file
@@ -0,0 +1,195 @@
|
||||
"""
|
||||
Tests für Inference Service.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import AsyncMock, patch, MagicMock
|
||||
from llm_gateway.services.inference import (
|
||||
InferenceService,
|
||||
InferenceResult,
|
||||
get_inference_service,
|
||||
)
|
||||
from llm_gateway.models.chat import (
|
||||
ChatCompletionRequest,
|
||||
ChatMessage,
|
||||
Usage,
|
||||
)
|
||||
|
||||
|
||||
class TestInferenceServiceModelMapping:
|
||||
"""Tests für Model Mapping."""
|
||||
|
||||
def setup_method(self):
|
||||
"""Setup für jeden Test."""
|
||||
self.service = InferenceService()
|
||||
|
||||
def test_map_breakpilot_model_to_ollama(self):
|
||||
"""Test Mapping von BreakPilot Modell zu Ollama."""
|
||||
# Mock Ollama als verfügbares Backend
|
||||
with patch.object(self.service, 'config') as mock_config:
|
||||
mock_config.ollama = MagicMock()
|
||||
mock_config.ollama.name = "ollama"
|
||||
mock_config.ollama.enabled = True
|
||||
mock_config.vllm = None
|
||||
mock_config.anthropic = None
|
||||
mock_config.backend_priority = ["ollama", "vllm", "anthropic"]
|
||||
|
||||
actual_model, backend = self.service._map_model_to_backend("breakpilot-teacher-8b")
|
||||
assert actual_model == "llama3.1:8b"
|
||||
assert backend.name == "ollama"
|
||||
|
||||
def test_map_breakpilot_70b_model(self):
|
||||
"""Test Mapping von 70B Modell."""
|
||||
with patch.object(self.service, 'config') as mock_config:
|
||||
mock_config.ollama = MagicMock()
|
||||
mock_config.ollama.name = "ollama"
|
||||
mock_config.ollama.enabled = True
|
||||
mock_config.vllm = None
|
||||
mock_config.anthropic = None
|
||||
mock_config.backend_priority = ["ollama"]
|
||||
|
||||
actual_model, backend = self.service._map_model_to_backend("breakpilot-teacher-70b")
|
||||
assert "70b" in actual_model.lower()
|
||||
|
||||
def test_map_claude_model_to_anthropic(self):
|
||||
"""Test Mapping von Claude Modell zu Anthropic."""
|
||||
with patch.object(self.service, 'config') as mock_config:
|
||||
mock_config.ollama = None
|
||||
mock_config.vllm = None
|
||||
mock_config.anthropic = MagicMock()
|
||||
mock_config.anthropic.name = "anthropic"
|
||||
mock_config.anthropic.enabled = True
|
||||
mock_config.anthropic.default_model = "claude-3-5-sonnet-20241022"
|
||||
mock_config.backend_priority = ["anthropic"]
|
||||
|
||||
actual_model, backend = self.service._map_model_to_backend("claude-3-5-sonnet")
|
||||
assert backend.name == "anthropic"
|
||||
assert "claude" in actual_model.lower()
|
||||
|
||||
def test_map_model_no_backend_available(self):
|
||||
"""Test Fehler wenn kein Backend verfügbar."""
|
||||
with patch.object(self.service, 'config') as mock_config:
|
||||
mock_config.ollama = None
|
||||
mock_config.vllm = None
|
||||
mock_config.anthropic = None
|
||||
mock_config.backend_priority = []
|
||||
|
||||
with pytest.raises(ValueError, match="No LLM backend available"):
|
||||
self.service._map_model_to_backend("breakpilot-teacher-8b")
|
||||
|
||||
|
||||
class TestInferenceServiceBackendSelection:
|
||||
"""Tests für Backend-Auswahl."""
|
||||
|
||||
def setup_method(self):
|
||||
"""Setup für jeden Test."""
|
||||
self.service = InferenceService()
|
||||
|
||||
def test_get_available_backend_priority(self):
|
||||
"""Test Backend-Auswahl nach Priorität."""
|
||||
with patch.object(self.service, 'config') as mock_config:
|
||||
# Beide Backends verfügbar
|
||||
mock_config.ollama = MagicMock()
|
||||
mock_config.ollama.enabled = True
|
||||
mock_config.vllm = MagicMock()
|
||||
mock_config.vllm.enabled = True
|
||||
mock_config.anthropic = None
|
||||
mock_config.backend_priority = ["vllm", "ollama"]
|
||||
|
||||
backend = self.service._get_available_backend()
|
||||
# vLLM hat höhere Priorität
|
||||
assert backend == mock_config.vllm
|
||||
|
||||
def test_get_available_backend_fallback(self):
|
||||
"""Test Fallback wenn primäres Backend nicht verfügbar."""
|
||||
with patch.object(self.service, 'config') as mock_config:
|
||||
mock_config.ollama = MagicMock()
|
||||
mock_config.ollama.enabled = True
|
||||
mock_config.vllm = MagicMock()
|
||||
mock_config.vllm.enabled = False # Deaktiviert
|
||||
mock_config.anthropic = None
|
||||
mock_config.backend_priority = ["vllm", "ollama"]
|
||||
|
||||
backend = self.service._get_available_backend()
|
||||
# Ollama als Fallback
|
||||
assert backend == mock_config.ollama
|
||||
|
||||
def test_get_available_backend_none_available(self):
|
||||
"""Test wenn kein Backend verfügbar."""
|
||||
with patch.object(self.service, 'config') as mock_config:
|
||||
mock_config.ollama = None
|
||||
mock_config.vllm = None
|
||||
mock_config.anthropic = None
|
||||
mock_config.backend_priority = ["ollama", "vllm", "anthropic"]
|
||||
|
||||
backend = self.service._get_available_backend()
|
||||
assert backend is None
|
||||
|
||||
|
||||
class TestInferenceResult:
|
||||
"""Tests für InferenceResult Dataclass."""
|
||||
|
||||
def test_inference_result_creation(self):
|
||||
"""Test InferenceResult erstellen."""
|
||||
result = InferenceResult(
|
||||
content="Hello, world!",
|
||||
model="llama3.1:8b",
|
||||
backend="ollama",
|
||||
usage=Usage(prompt_tokens=10, completion_tokens=5, total_tokens=15),
|
||||
finish_reason="stop",
|
||||
)
|
||||
assert result.content == "Hello, world!"
|
||||
assert result.model == "llama3.1:8b"
|
||||
assert result.backend == "ollama"
|
||||
assert result.usage.total_tokens == 15
|
||||
|
||||
def test_inference_result_defaults(self):
|
||||
"""Test Standardwerte."""
|
||||
result = InferenceResult(
|
||||
content="Test",
|
||||
model="test",
|
||||
backend="test",
|
||||
)
|
||||
assert result.usage is None
|
||||
assert result.finish_reason == "stop"
|
||||
|
||||
|
||||
class TestInferenceServiceComplete:
|
||||
"""Tests für complete() Methode."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_complete_calls_correct_backend(self):
|
||||
"""Test dass correct Backend aufgerufen wird."""
|
||||
service = InferenceService()
|
||||
|
||||
request = ChatCompletionRequest(
|
||||
model="breakpilot-teacher-8b",
|
||||
messages=[ChatMessage(role="user", content="Hello")],
|
||||
)
|
||||
|
||||
# Mock das Backend
|
||||
with patch.object(service, '_map_model_to_backend') as mock_map:
|
||||
with patch.object(service, '_call_ollama') as mock_call:
|
||||
mock_backend = MagicMock()
|
||||
mock_backend.name = "ollama"
|
||||
mock_map.return_value = ("llama3.1:8b", mock_backend)
|
||||
mock_call.return_value = InferenceResult(
|
||||
content="Hello!",
|
||||
model="llama3.1:8b",
|
||||
backend="ollama",
|
||||
)
|
||||
|
||||
response = await service.complete(request)
|
||||
|
||||
mock_call.assert_called_once()
|
||||
assert response.choices[0].message.content == "Hello!"
|
||||
|
||||
|
||||
class TestGetInferenceServiceSingleton:
|
||||
"""Tests für Singleton Pattern."""
|
||||
|
||||
def test_singleton_returns_same_instance(self):
|
||||
"""Test dass get_inference_service Singleton zurückgibt."""
|
||||
service1 = get_inference_service()
|
||||
service2 = get_inference_service()
|
||||
assert service1 is service2
|
||||
Reference in New Issue
Block a user