mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-08-29 04:26:20 +00:00
178 lines
5.8 KiB
Python
178 lines
5.8 KiB
Python
|
|
import pytest
|
|
from unittest.mock import MagicMock, patch
|
|
from typing import Type, List, Optional
|
|
from pydantic import BaseModel, ValidationError
|
|
|
|
from semantica.semantic_extract.providers import BaseProvider
|
|
from semantica.semantic_extract.methods import (
|
|
extract_entities_llm,
|
|
extract_relations_llm,
|
|
extract_triplets_llm
|
|
)
|
|
from semantica.semantic_extract.schemas import EntitiesResponse, RelationsResponse, TripletsResponse
|
|
from semantica.semantic_extract.ner_extractor import Entity
|
|
from semantica.semantic_extract.relation_extractor import Relation
|
|
|
|
# Mock Pydantic models for responses
|
|
class MockEntity(BaseModel):
|
|
text: str
|
|
label: str
|
|
start: int = 0
|
|
end: int = 0
|
|
confidence: float = 1.0
|
|
|
|
class MockEntitiesResponse(BaseModel):
|
|
entities: List[MockEntity]
|
|
|
|
class MockRelation(BaseModel):
|
|
subject: str
|
|
predicate: str
|
|
object: str
|
|
confidence: float = 1.0
|
|
|
|
class MockRelationsResponse(BaseModel):
|
|
relations: List[MockRelation]
|
|
|
|
class MockTriplet(BaseModel):
|
|
subject: str
|
|
predicate: str
|
|
object: str
|
|
confidence: float = 1.0
|
|
|
|
class MockTripletsResponse(BaseModel):
|
|
triplets: List[MockTriplet]
|
|
|
|
class MockProvider(BaseProvider):
|
|
def __init__(self, **kwargs):
|
|
super().__init__(**kwargs)
|
|
self.generate_typed_called = False
|
|
self.generate_structured_called = False
|
|
self.model = "mock-model"
|
|
self.is_available_val = True
|
|
|
|
def is_available(self) -> bool:
|
|
return self.is_available_val
|
|
|
|
def generate(self, prompt: str, **kwargs) -> str:
|
|
return "{}"
|
|
|
|
def generate_structured(self, prompt: str, **kwargs) -> dict:
|
|
self.generate_structured_called = True
|
|
if "entities" in prompt.lower():
|
|
return [{"text": "Apple", "label": "ORG", "start": 0, "end": 5}]
|
|
elif "relations" in prompt.lower():
|
|
return [{"subject": "Steve Jobs", "predicate": "founded", "object": "Apple"}]
|
|
elif "triplets" in prompt.lower():
|
|
return [{"subject": "Steve Jobs", "predicate": "founded", "object": "Apple"}]
|
|
return {}
|
|
|
|
def generate_typed(
|
|
self,
|
|
prompt: str,
|
|
schema: Type[BaseModel],
|
|
max_retries: int = 3,
|
|
**kwargs
|
|
) -> BaseModel:
|
|
self.generate_typed_called = True
|
|
|
|
if schema.__name__ == "EntitiesResponse":
|
|
return EntitiesResponse(entities=[
|
|
{"text": "Apple", "label": "ORG", "start_char": 0, "end_char": 5, "confidence": 0.99}
|
|
])
|
|
elif schema.__name__ == "RelationsResponse":
|
|
return RelationsResponse(relations=[
|
|
{"subject": "Steve Jobs", "predicate": "founded", "object": "Apple", "confidence": 0.95}
|
|
])
|
|
elif schema.__name__ == "TripletsResponse":
|
|
return TripletsResponse(triplets=[
|
|
{"subject": "Steve Jobs", "predicate": "founded", "object": "Apple", "confidence": 0.95}
|
|
])
|
|
return schema()
|
|
|
|
@pytest.fixture
|
|
def mock_provider():
|
|
return MockProvider()
|
|
|
|
@patch("semantica.semantic_extract.methods.create_provider")
|
|
def test_extract_entities_typed(mock_create_provider, mock_provider):
|
|
mock_create_provider.return_value = mock_provider
|
|
|
|
text = "Apple was founded by Steve Jobs."
|
|
entities = extract_entities_llm(
|
|
text,
|
|
provider="mock",
|
|
structured_output_mode="typed"
|
|
)
|
|
|
|
assert mock_provider.generate_typed_called
|
|
assert len(entities) == 1
|
|
assert entities[0].text == "Apple"
|
|
assert entities[0].label == "ORG"
|
|
assert entities[0].metadata["extraction_method"] == "llm_typed"
|
|
|
|
@patch("semantica.semantic_extract.methods.create_provider")
|
|
def test_extract_entities_legacy(mock_create_provider, mock_provider):
|
|
mock_create_provider.return_value = mock_provider
|
|
|
|
text = "Apple was founded by Steve Jobs."
|
|
entities = extract_entities_llm(
|
|
text,
|
|
provider="mock",
|
|
structured_output_mode="legacy"
|
|
)
|
|
|
|
# Legacy mode now redirects to typed mode
|
|
assert mock_provider.generate_typed_called
|
|
assert not mock_provider.generate_structured_called
|
|
assert len(entities) == 1
|
|
assert entities[0].text == "Apple"
|
|
assert entities[0].label == "ORG"
|
|
assert entities[0].metadata["extraction_method"] == "llm_typed"
|
|
|
|
@patch("semantica.semantic_extract.methods.create_provider")
|
|
def test_extract_relations_typed(mock_create_provider, mock_provider):
|
|
mock_create_provider.return_value = mock_provider
|
|
|
|
text = "Steve Jobs founded Apple."
|
|
entities = [
|
|
Entity(text="Steve Jobs", label="PERSON", start_char=0, end_char=10),
|
|
Entity(text="Apple", label="ORG", start_char=19, end_char=24)
|
|
]
|
|
|
|
relations = extract_relations_llm(
|
|
text,
|
|
entities=entities,
|
|
provider="mock",
|
|
structured_output_mode="typed"
|
|
)
|
|
|
|
assert mock_provider.generate_typed_called
|
|
assert len(relations) == 1
|
|
assert relations[0].subject.text == "Steve Jobs"
|
|
assert relations[0].object.text == "Apple"
|
|
assert relations[0].predicate == "founded"
|
|
assert relations[0].metadata["extraction_method"] == "llm_typed"
|
|
|
|
@patch("semantica.semantic_extract.methods.create_provider")
|
|
def test_extract_triplets_typed(mock_create_provider, mock_provider):
|
|
mock_create_provider.return_value = mock_provider
|
|
|
|
text = "Steve Jobs founded Apple."
|
|
|
|
triplets = extract_triplets_llm(
|
|
text,
|
|
provider="mock",
|
|
structured_output_mode="typed"
|
|
)
|
|
|
|
assert mock_provider.generate_typed_called
|
|
assert len(triplets) == 1
|
|
assert triplets[0].subject == "Steve Jobs"
|
|
assert triplets[0].object == "Apple"
|
|
assert triplets[0].predicate == "founded"
|
|
assert triplets[0].metadata["extraction_method"] == "llm_typed"
|
|
|
|
if __name__ == "__main__":
|
|
pytest.main([__file__])
|