@ -14,6 +14,31 @@ logger = logging.getLogger(__name__)
# Essa classe concentra normalizacao tecnica e coercoes estruturadas.
# A semantica conversacional idealmente vem do modelo, nao daqui.
class EntityNormalizer :
_TURN_INTENT_ALIASES = {
" create_order " : " order_create " ,
" place_order " : " order_create " ,
" new_order " : " order_create " ,
" buy_car " : " order_create " ,
" purchase_car " : " order_create " ,
" cancel_order " : " order_cancel " ,
" list_inventory " : " inventory_search " ,
" search_inventory " : " inventory_search " ,
" clear_conversation " : " conversation_reset " ,
}
_TURN_ACTION_ALIASES = {
" collect_order " : " collect_order_create " ,
" collect_review " : " collect_review_schedule " ,
" cancel_flow " : " cancel_active_flow " ,
" reset_context " : " clear_context " ,
}
_ORDER_MISSING_FIELD_ALIASES = {
" modelo_carro " : " vehicle_id " ,
" modelo_do_carro " : " vehicle_id " ,
" modelo_veiculo " : " vehicle_id " ,
" veiculo " : " vehicle_id " ,
" carro " : " vehicle_id " ,
}
def empty_turn_decision ( self ) - > dict :
return TurnDecision ( ) . model_dump ( )
@ -103,6 +128,7 @@ class EntityNormalizer:
def coerce_turn_decision ( self , payload ) - > dict :
if not isinstance ( payload , dict ) :
return self . empty_turn_decision ( )
payload = self . _normalize_turn_decision_payload ( payload )
try :
model = TurnDecision . model_validate ( payload )
except ValidationError :
@ -122,6 +148,61 @@ class EntityNormalizer:
dumped [ " missing_fields " ] = [ str ( field ) for field in dumped . get ( " missing_fields " ) or [ ] if str ( field ) . strip ( ) ]
return dumped
def _normalize_turn_decision_payload ( self , payload : dict ) - > dict :
normalized = dict ( payload )
raw_intent = self . normalize_text ( str ( normalized . get ( " intent " ) or " " ) ) . replace ( " - " , " _ " ) . replace ( " " , " _ " )
if raw_intent in self . _TURN_INTENT_ALIASES :
normalized [ " intent " ] = self . _TURN_INTENT_ALIASES [ raw_intent ]
raw_action = self . normalize_text ( str ( normalized . get ( " action " ) or " " ) ) . replace ( " - " , " _ " ) . replace ( " " , " _ " )
if raw_action in self . _TURN_ACTION_ALIASES :
normalized [ " action " ] = self . _TURN_ACTION_ALIASES [ raw_action ]
missing_fields = normalized . get ( " missing_fields " )
if isinstance ( missing_fields , list ) :
normalized [ " missing_fields " ] = self . _normalize_turn_missing_fields ( missing_fields )
entities = normalized . get ( " entities " )
if isinstance ( entities , dict ) :
normalized [ " entities " ] = dict ( entities )
if self . _should_route_order_alias_to_collection ( normalized ) :
normalized [ " action " ] = " collect_order_create "
normalized [ " missing_fields " ] = [ ]
normalized [ " response_to_user " ] = None
return normalized
def _normalize_turn_missing_fields ( self , missing_fields : list ) - > list [ str ] :
normalized_fields : list [ str ] = [ ]
for field in missing_fields :
candidate = self . normalize_text ( str ( field or " " ) ) . replace ( " - " , " _ " ) . replace ( " " , " _ " )
canonical = self . _ORDER_MISSING_FIELD_ALIASES . get ( candidate , candidate )
if canonical and canonical not in normalized_fields :
normalized_fields . append ( canonical )
return normalized_fields
def _should_route_order_alias_to_collection ( self , payload : dict ) - > bool :
if payload . get ( " intent " ) != " order_create " :
return False
if payload . get ( " action " ) != " ask_missing_fields " :
return False
missing_fields = payload . get ( " missing_fields " ) or [ ]
if not missing_fields or any ( field != " vehicle_id " for field in missing_fields ) :
return False
entities = payload . get ( " entities " )
if not isinstance ( entities , dict ) :
return False
order_fields = entities . get ( " order_fields " )
if not isinstance ( order_fields , dict ) :
return False
if order_fields . get ( " vehicle_id " ) :
return False
return True
def normalize_text ( self , text : str ) - > str :
return technical_normalizer . normalize_text ( text )