Phase 3 - State Transformation: - transform_state() function with alpha/beta parameters - compute_adaptive_params() for dynamic transformation - StateTransformer class for state management Phase 4 - Occasion Logger: - OccasionLog dataclass for structured logging - OccasionLogger for JSON file storage - Profile evolution tracking and statistics Phase 5 - Occasion Manager: - Full cycle: Prehension → Concrescence → Satisfaction - Search integration (thoughts, library) - State creation and logging orchestration Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
141 lines
4.5 KiB
Python
141 lines
4.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Tests pour Phase 3 - Transformation d'état."""
|
|
|
|
import numpy as np
|
|
import pytest
|
|
|
|
from ..state_transformation import (
|
|
transform_state,
|
|
compute_adaptive_params,
|
|
StateTransformer
|
|
)
|
|
|
|
|
|
class TestTransformState:
|
|
"""Tests de la fonction de transformation."""
|
|
|
|
def test_transform_preserves_norm(self):
|
|
"""Le vecteur transformé doit rester normalisé."""
|
|
s_prev = np.random.randn(1024)
|
|
s_prev = s_prev / np.linalg.norm(s_prev)
|
|
|
|
occasion = np.random.randn(1024)
|
|
occasion = occasion / np.linalg.norm(occasion)
|
|
|
|
s_new = transform_state(s_prev, occasion)
|
|
|
|
assert abs(np.linalg.norm(s_new) - 1.0) < 0.001
|
|
|
|
def test_high_alpha_preserves_identity(self):
|
|
"""Alpha élevé = peu de changement."""
|
|
s_prev = np.random.randn(1024)
|
|
s_prev = s_prev / np.linalg.norm(s_prev)
|
|
|
|
occasion = np.random.randn(1024)
|
|
occasion = occasion / np.linalg.norm(occasion)
|
|
|
|
s_new = transform_state(s_prev, occasion, alpha=0.99, beta=0.01)
|
|
|
|
similarity = np.dot(s_prev, s_new)
|
|
assert similarity > 0.98, f"Trop de changement: similarity={similarity}"
|
|
|
|
def test_low_alpha_allows_change(self):
|
|
"""Alpha bas = plus de changement."""
|
|
s_prev = np.random.randn(1024)
|
|
s_prev = s_prev / np.linalg.norm(s_prev)
|
|
|
|
# Occasion très différente
|
|
occasion = -s_prev + 0.1 * np.random.randn(1024)
|
|
occasion = occasion / np.linalg.norm(occasion)
|
|
|
|
s_new = transform_state(s_prev, occasion, alpha=0.5, beta=0.5)
|
|
|
|
similarity = np.dot(s_prev, s_new)
|
|
assert similarity < 0.9, f"Pas assez de changement: similarity={similarity}"
|
|
|
|
def test_identical_occasion_increases_identity(self):
|
|
"""Si l'occasion est identique à l'état, l'identité est renforcée."""
|
|
s_prev = np.random.randn(1024)
|
|
s_prev = s_prev / np.linalg.norm(s_prev)
|
|
|
|
s_new = transform_state(s_prev, s_prev.copy(), alpha=0.85, beta=0.15)
|
|
|
|
# Doit rester très similaire
|
|
similarity = np.dot(s_prev, s_new)
|
|
assert similarity > 0.99
|
|
|
|
|
|
class TestAdaptiveParams:
|
|
"""Tests des paramètres adaptatifs."""
|
|
|
|
def test_default_params(self):
|
|
"""Paramètres par défaut."""
|
|
alpha, beta = compute_adaptive_params({})
|
|
assert abs(alpha + beta - 1.0) < 0.001
|
|
assert 0.8 < alpha < 0.9
|
|
assert 0.1 < beta < 0.2
|
|
|
|
def test_more_thoughts_increases_beta(self):
|
|
"""Plus de pensées = plus de beta."""
|
|
alpha1, beta1 = compute_adaptive_params({'thoughts_created': 0})
|
|
alpha2, beta2 = compute_adaptive_params({'thoughts_created': 5})
|
|
|
|
assert beta2 > beta1
|
|
assert alpha2 < alpha1
|
|
|
|
def test_timer_reduces_intensity(self):
|
|
"""Timer = moins d'intensité."""
|
|
alpha_user, beta_user = compute_adaptive_params({
|
|
'trigger_type': 'user',
|
|
'thoughts_created': 3
|
|
})
|
|
alpha_timer, beta_timer = compute_adaptive_params({
|
|
'trigger_type': 'timer',
|
|
'thoughts_created': 3
|
|
})
|
|
|
|
assert beta_timer < beta_user
|
|
assert alpha_timer > alpha_user
|
|
|
|
def test_params_sum_to_one(self):
|
|
"""Alpha + beta = 1 toujours."""
|
|
test_cases = [
|
|
{'thoughts_created': 0},
|
|
{'thoughts_created': 10},
|
|
{'trigger_type': 'timer'},
|
|
{'trigger_type': 'user', 'trigger_content': 'x' * 300},
|
|
]
|
|
|
|
for case in test_cases:
|
|
alpha, beta = compute_adaptive_params(case)
|
|
assert abs(alpha + beta - 1.0) < 0.001, f"Cas: {case}"
|
|
|
|
|
|
class TestStateTransformer:
|
|
"""Tests du StateTransformer (nécessite Weaviate)."""
|
|
|
|
@pytest.fixture
|
|
def transformer(self):
|
|
"""Créer un transformer sans modèle (tests unitaires)."""
|
|
return StateTransformer(embedding_model=None)
|
|
|
|
def test_get_current_state_id(self, transformer):
|
|
"""Test de récupération de l'ID courant."""
|
|
# Ce test nécessite Weaviate
|
|
state_id = transformer.get_current_state_id()
|
|
assert isinstance(state_id, int)
|
|
# -1 si pas d'état, sinon >= 0
|
|
assert state_id >= -1
|
|
|
|
@pytest.mark.skip(reason="Nécessite Weaviate avec S(0)")
|
|
def test_get_state_vector(self, transformer):
|
|
"""Test de récupération du vecteur d'état."""
|
|
vector = transformer.get_state_vector(0)
|
|
if vector is not None:
|
|
assert len(vector) == 1024
|
|
assert abs(np.linalg.norm(vector) - 1.0) < 0.01
|
|
|
|
|
|
if __name__ == "__main__":
|
|
pytest.main([__file__, "-v"])
|