146 lines
4.9 KiB
Python
146 lines
4.9 KiB
Python
from lead_scoring.schemas.snapshot import CanonicalSnapshot
|
|
from lead_scoring.scoring import (
|
|
BehavioralMetric,
|
|
RuleMetric,
|
|
ScoringConfig,
|
|
ScoringRule,
|
|
compute_behavioral_metrics,
|
|
evaluate_rule_score,
|
|
extract_normalized_features,
|
|
)
|
|
|
|
|
|
def make_snapshot() -> CanonicalSnapshot:
|
|
return CanonicalSnapshot.model_validate(
|
|
{
|
|
"captured_at": "2026-08-27T12:00:00Z",
|
|
"lead_profile": {
|
|
"ems_lead_id": "lead-1",
|
|
"name": "Lead One",
|
|
"phone": "+84900000000",
|
|
},
|
|
"conversations": [
|
|
{
|
|
"conversation_id": "conversation-1",
|
|
"messages": [
|
|
{
|
|
"message_id": "message-1",
|
|
"sender_type": "contact",
|
|
"content": "Tôi cần tư vấn",
|
|
"sent_at": "2026-08-27T11:00:00Z",
|
|
}
|
|
],
|
|
}
|
|
],
|
|
}
|
|
)
|
|
|
|
|
|
def test_rule_score_is_deterministic_and_bounded_by_immutable_config() -> None:
|
|
snapshot = make_snapshot()
|
|
config = ScoringConfig(
|
|
version="fixture-1",
|
|
base_score=95,
|
|
confidence=0.75,
|
|
confidence_threshold=0.8,
|
|
hitl_policy_version="fixture-policy-1",
|
|
rules=(
|
|
ScoringRule(metric=RuleMetric.PROFILE_FIELD_COUNT, minimum=2, points=20),
|
|
ScoringRule(metric=RuleMetric.MESSAGE_COUNT, minimum=2, points=-10),
|
|
),
|
|
)
|
|
|
|
assert evaluate_rule_score(snapshot, config) == 100
|
|
assert evaluate_rule_score(snapshot, config) == 100
|
|
assert config.model_config.get("frozen") is True
|
|
|
|
|
|
def test_rule_score_clamps_negative_total_to_zero() -> None:
|
|
config = ScoringConfig(
|
|
version="fixture-2",
|
|
confidence=0.5,
|
|
confidence_threshold=0.8,
|
|
hitl_policy_version="fixture-policy-2",
|
|
rules=(ScoringRule(metric=RuleMetric.CONVERSATION_COUNT, minimum=1, points=-100),),
|
|
)
|
|
|
|
assert evaluate_rule_score(make_snapshot(), config) == 0
|
|
|
|
|
|
def test_compute_behavioral_metrics_deterministic_and_selective() -> None:
|
|
snapshot = CanonicalSnapshot.model_validate(
|
|
{
|
|
"captured_at": "2026-08-27T12:00:00Z",
|
|
"lead_profile": {
|
|
"ems_lead_id": "lead-behavioral-1",
|
|
"name": "Test Lead",
|
|
},
|
|
"conversations": [
|
|
{
|
|
"conversation_id": "conv-1",
|
|
"messages": [
|
|
{
|
|
"message_id": "m1",
|
|
"sender_type": "agent",
|
|
"content": "Chào anh",
|
|
"sent_at": "2026-08-27T08:00:00Z",
|
|
},
|
|
{
|
|
"message_id": "m2",
|
|
"sender_type": "contact",
|
|
"content": "Tôi muốn đăng ký",
|
|
"sent_at": "2026-08-27T08:05:00Z",
|
|
},
|
|
{
|
|
"message_id": "m3",
|
|
"sender_type": "agent",
|
|
"content": "Vâng ạ",
|
|
"sent_at": "2026-08-27T08:06:00Z",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
"conversation_id": "conv-2",
|
|
"messages": [
|
|
{
|
|
"message_id": "m4",
|
|
"sender_type": "contact",
|
|
"content": "Hôm nay tôi muốn hỏi thêm học phí",
|
|
"sent_at": "2026-08-27T10:00:00Z",
|
|
},
|
|
],
|
|
},
|
|
],
|
|
}
|
|
)
|
|
|
|
all_metrics = compute_behavioral_metrics(snapshot)
|
|
assert all_metrics["message_count"] == 4
|
|
assert all_metrics["conversation_count"] == 2
|
|
assert (
|
|
all_metrics["conversation_turn_count"] == 4
|
|
) # conv1: 3 turns (agent->contact->agent) + conv2: 1 turn (contact) = 4
|
|
assert all_metrics["customer_message_count"] == 2
|
|
assert all_metrics["customer_initiated_return"] is True
|
|
|
|
selected_metrics = compute_behavioral_metrics(
|
|
snapshot,
|
|
[
|
|
BehavioralMetric.CONVERSATION_TURN_COUNT,
|
|
BehavioralMetric.CUSTOMER_INITIATED_RETURN,
|
|
],
|
|
)
|
|
assert set(selected_metrics.keys()) == {
|
|
"conversation_turn_count",
|
|
"customer_initiated_return",
|
|
}
|
|
assert selected_metrics["conversation_turn_count"] == 4
|
|
assert selected_metrics["customer_initiated_return"] is True
|
|
|
|
features = extract_normalized_features(snapshot)
|
|
assert features.message_count == 4
|
|
assert features.conversation_count == 2
|
|
assert features.conversation_turn_count == 4
|
|
assert features.customer_message_count == 2
|
|
assert features.customer_initiated_return is True
|