@ -77,10 +77,12 @@ class FakeRegistry:
if self . raise_http_exception is not None :
raise self . raise_http_exception
if tool_name == " consultar_estoque " :
return [
stock_results = [
{ " id " : 1 , " modelo " : " Honda Civic 2021 " , " categoria " : " sedan " , " preco " : 48500.0 } ,
{ " id " : 2 , " modelo " : " Toyota Yaris 2020 " , " categoria " : " hatch " , " preco " : 49900.0 } ,
]
reverse = str ( arguments . get ( " ordenar_preco " ) or " asc " ) . lower ( ) == " desc "
return sorted ( stock_results , key = lambda item : float ( item [ " preco " ] ) , reverse = reverse )
if tool_name == " listar_pedidos " :
return [
{
@ -190,7 +192,7 @@ class OrderFlowHarness(OrderFlowMixin):
def _is_negative_message ( self , text : str ) - > bool :
normalized = self . normalizer . normalize_text ( text ) . strip ( ) . rstrip ( " .!?,;: " )
return normalized in { " nao " , " n ã o" , " negativo " , " cancelar " , " outro " , " outra opcao " , " outra op çã o" }
return normalized in { " nao " , " n \u00e3 o" , " negativo " , " cancelar " , " outro " , " outra opcao " , " outra op \u00e7 \u00e3 o" }
def _normalize_positive_number ( self , value ) :
return self . normalizer . normalize_positive_number ( value )
@ -377,12 +379,12 @@ class ConversationAdjustmentsTests(unittest.TestCase):
normalizer = EntityNormalizer ( )
self . assertEqual (
normalizer . normalize_datetime_connector ( " 10/03/2026 à s 09:00" ) ,
normalizer . normalize_datetime_connector ( " 10/03/2026 \u00e0 s 09:00" ) ,
" 10/03/2026 09:00 " ,
)
def test_parse_review_datetime_accepts_as_com_acento ( self ) :
parsed = _parse_data_hora_revisao ( " 10/03/2026 à s 09:00" )
parsed = _parse_data_hora_revisao ( " 10/03/2026 \u00e0 s 09:00" )
self . assertEqual ( parsed , datetime ( 2026 , 3 , 10 , 9 , 0 ) )
@ -409,11 +411,11 @@ class ConversationAdjustmentsTests(unittest.TestCase):
state = FakeState ( )
policy = ConversationPolicy ( service = FakeService ( state ) )
message = " Esque ça as operações anteriores, agora quero agendar revisã o para ABC1234"
message = " Esque \u00e7 a as opera \u00e7 \u00f5 es anteriores, agora quero agendar revis \u00e3 o para ABC1234"
cleaned = policy . remove_order_selection_reset_prefix ( message )
self . assertTrue ( policy . is_order_selection_reset_message ( message ) )
self . assertEqual ( cleaned , " quero agendar revis ã o para ABC1234" )
self . assertEqual ( cleaned , " quero agendar revis \u00e3 o para ABC1234" )
class CancelOrderFlowTests ( unittest . IsolatedAsyncioTestCase ) :
@ -657,6 +659,72 @@ class CreateOrderFlowWithVehicleTests(unittest.IsolatedAsyncioTestCase):
self . assertIn ( " Encontrei 2 veiculo(s): " , response )
self . assertIn ( " Honda Civic 2021 " , response )
async def test_order_flow_budget_search_prioritizes_matches_closest_to_ceiling ( self ) :
state = FakeState (
contexts = {
10 : {
" generic_memory " : { " cpf " : " 12345678909 " , " orcamento_max " : 50000 } ,
" last_stock_results " : [ ] ,
" selected_vehicle " : None ,
}
}
)
registry = FakeRegistry ( )
flow = OrderFlowHarness ( state = state , registry = registry )
async def fake_hydrate_mock_customer_from_cpf ( cpf : str , user_id : int | None = None ) :
return { " cpf " : cpf , " user_id " : user_id }
with patch (
" app.services.flows.order_flow.hydrate_mock_customer_from_cpf " ,
new = fake_hydrate_mock_customer_from_cpf ,
) :
response = await flow . _try_collect_and_create_order (
message = " Quero comprar um carro de ate 50 mil, meu CPF e 12345678909 " ,
user_id = 10 ,
extracted_fields = { " cpf " : " 12345678909 " } ,
intents = { } ,
turn_decision = { " intent " : " order_create " , " domain " : " sales " , " action " : " collect_order_create " } ,
)
self . assertEqual ( registry . calls [ 0 ] [ 1 ] [ " ordenar_preco " ] , " desc " )
self . assertIn ( " 1. Toyota Yaris 2020 (hatch) - R$ 49900.00 " , response )
self . assertIn ( " 2. Honda Civic 2021 (sedan) - R$ 48500.00 " , response )
self . assertEqual ( flow . _get_last_stock_results ( user_id = 10 ) [ 0 ] [ " modelo " ] , " Toyota Yaris 2020 " )
async def test_order_flow_budget_search_keeps_cheapest_first_when_user_asks_for_cheapest ( self ) :
state = FakeState (
contexts = {
10 : {
" generic_memory " : { " cpf " : " 12345678909 " , " orcamento_max " : 50000 } ,
" last_stock_results " : [ ] ,
" selected_vehicle " : None ,
}
}
)
registry = FakeRegistry ( )
flow = OrderFlowHarness ( state = state , registry = registry )
async def fake_hydrate_mock_customer_from_cpf ( cpf : str , user_id : int | None = None ) :
return { " cpf " : cpf , " user_id " : user_id }
with patch (
" app.services.flows.order_flow.hydrate_mock_customer_from_cpf " ,
new = fake_hydrate_mock_customer_from_cpf ,
) :
response = await flow . _try_collect_and_create_order (
message = " Quero comprar o carro mais barato ate 50 mil, meu CPF e 12345678909 " ,
user_id = 10 ,
extracted_fields = { " cpf " : " 12345678909 " } ,
intents = { } ,
turn_decision = { " intent " : " order_create " , " domain " : " sales " , " action " : " collect_order_create " } ,
)
self . assertEqual ( registry . calls [ 0 ] [ 1 ] [ " ordenar_preco " ] , " asc " )
self . assertIn ( " 1. Honda Civic 2021 (sedan) - R$ 48500.00 " , response )
self . assertIn ( " 2. Toyota Yaris 2020 (hatch) - R$ 49900.00 " , response )
self . assertEqual ( flow . _get_last_stock_results ( user_id = 10 ) [ 0 ] [ " modelo " ] , " Honda Civic 2021 " )
async def test_order_flow_extracts_budget_from_message_when_llm_misses_it ( self ) :
state = FakeState (
contexts = {
@ -1099,6 +1167,40 @@ class CreateOrderFlowWithVehicleTests(unittest.IsolatedAsyncioTestCase):
self . assertEqual ( arguments [ " cpf " ] , " 12345678909 " )
self . assertIn ( " Veiculo: Hyundai HB20S 2022 " , second_response )
async def test_order_flow_accepts_partial_model_reference_from_last_stock_results ( self ) :
state = FakeState (
contexts = {
10 : {
" active_domain " : " sales " ,
" generic_memory " : { " orcamento_max " : 70000 } ,
" shared_memory " : { " orcamento_max " : 70000 } ,
" last_stock_results " : [
{ " id " : 15 , " modelo " : " Volkswagen T-Cross 2024 " , " categoria " : " hatch " , " preco " : 59306.0 } ,
{ " id " : 16 , " modelo " : " Volkswagen T-Cross 2024 " , " categoria " : " hatch " , " preco " : 59306.0 } ,
{ " id " : 2 , " modelo " : " Toyota Corolla 2020 " , " categoria " : " hatch " , " preco " : 58476.0 } ,
] ,
" selected_vehicle " : None ,
}
}
)
registry = FakeRegistry ( )
flow = OrderFlowHarness ( state = state , registry = registry )
response = await flow . _try_collect_and_create_order (
message = " gostaria de fazer o pedido do Volkswagen T-Cros " ,
user_id = 10 ,
extracted_fields = { } ,
intents = { " order_create " : True } ,
)
draft = state . get_entry ( " pending_order_drafts " , 10 )
self . assertIsNotNone ( draft )
self . assertEqual ( draft [ " payload " ] [ " vehicle_id " ] , 15 )
self . assertEqual ( draft [ " payload " ] [ " modelo_veiculo " ] , " Volkswagen T-Cross 2024 " )
self . assertEqual ( state . get_user_context ( 10 ) [ " selected_vehicle " ] [ " id " ] , 15 )
self . assertIn ( " cpf do cliente " , response . lower ( ) )
self . assertEqual ( registry . calls , [ ] )
async def test_order_flow_bootstraps_selection_from_last_stock_results_without_repeating_order_verb ( self ) :
state = FakeState (
contexts = {
@ -2625,3 +2727,4 @@ class ToolRegistryExecutionTests(unittest.IsolatedAsyncioTestCase):
if __name__ == " __main__ " :
unittest . main ( )