@ -276,6 +276,35 @@ class TurnDecisionContractTests(unittest.IsolatedAsyncioTestCase):
self . assertEqual ( decision [ " tool_arguments " ] [ " numero_pedido " ] , " PED-20260310113756-DC1540 " )
self . assertEqual ( decision [ " tool_arguments " ] [ " motivo " ] , " desisti da compra " )
def test_coerce_turn_decision_normalizes_order_tool_name_alias_and_downgrades_incomplete_call ( self ) :
normalizer = EntityNormalizer ( )
decision = normalizer . coerce_turn_decision (
{
" intent " : " order_create " ,
" domain " : " sales " ,
" action " : " call_tool " ,
" tool_name " : " fazer_pedido " ,
" tool_arguments " : {
" modelo " : " Onix 2024 " ,
} ,
" entities " : {
" generic_memory " : { } ,
" review_fields " : { } ,
" review_management_fields " : { } ,
" order_fields " : { } ,
" cancel_order_fields " : { } ,
} ,
" missing_fields " : [ ] ,
" response_to_user " : None ,
}
)
self . assertEqual ( decision [ " action " ] , " collect_order_create " )
self . assertIsNone ( decision [ " tool_name " ] )
self . assertEqual ( decision [ " tool_arguments " ] , { } )
self . assertEqual ( decision [ " entities " ] [ " order_fields " ] [ " modelo_veiculo " ] , " Onix 2024 " )
def test_coerce_turn_decision_normalizes_review_tool_name_alias ( self ) :
normalizer = EntityNormalizer ( )
@ -570,6 +599,88 @@ class TurnDecisionContractTests(unittest.IsolatedAsyncioTestCase):
self . assertEqual ( merged [ " generic_memory " ] [ " orcamento_max " ] , 70000 )
self . assertEqual ( merged [ " order_fields " ] [ " cpf " ] , " 12345678909 " )
def test_capture_successful_review_tool_side_effects_store_last_review_package_for_direct_tool_call ( self ) :
state = FakeState (
contexts = {
7 : {
" active_domain " : " review " ,
" generic_memory " : { } ,
" shared_memory " : { } ,
" order_queue " : [ ] ,
" pending_order_selection " : None ,
" pending_switch " : None ,
" last_stock_results " : [ ] ,
" selected_vehicle " : None ,
}
}
)
service = OrquestradorService . __new__ ( OrquestradorService )
service . state = state
service . normalizer = EntityNormalizer ( )
service . _get_user_context = lambda user_id : state . get_user_context ( user_id )
service . _save_user_context = lambda user_id , context : state . save_user_context ( user_id , context )
service . _capture_successful_tool_side_effects (
tool_name = " agendar_revisao " ,
arguments = {
" placa " : " ABC1463 " ,
" data_hora " : " 28/04/2026 15:00 " ,
" modelo " : " Civic " ,
" ano " : 2024 ,
" km " : 30000 ,
" revisao_previa_concessionaria " : True ,
} ,
tool_result = {
" protocolo " : " REV-TESTE-123 " ,
" placa " : " ABC1463 " ,
" data_hora " : " 28/04/2026 15:00 " ,
} ,
user_id = 7 ,
)
cached = state . get_entry ( " last_review_packages " , 7 )
self . assertIsNotNone ( cached )
self . assertEqual ( cached [ " payload " ] [ " placa " ] , " ABC1463 " )
self . assertEqual ( cached [ " payload " ] [ " modelo " ] , " Civic " )
self . assertTrue ( cached [ " payload " ] [ " revisao_previa_concessionaria " ] )
def test_capture_tool_result_context_stores_pending_stock_selection_entry ( self ) :
state = FakeState (
contexts = {
5 : {
" active_domain " : " sales " ,
" generic_memory " : { } ,
" shared_memory " : { } ,
" order_queue " : [ ] ,
" pending_order_selection " : None ,
" pending_switch " : None ,
" last_stock_results " : [ ] ,
" selected_vehicle " : None ,
}
}
)
service = OrquestradorService . __new__ ( OrquestradorService )
service . state = state
service . normalizer = EntityNormalizer ( )
service . _get_user_context = lambda user_id : state . get_user_context ( user_id )
service . _save_user_context = lambda user_id , context : state . save_user_context ( user_id , context )
service . _capture_tool_result_context (
tool_name = " consultar_estoque " ,
tool_result = [
{ " id " : 15 , " modelo " : " Volkswagen T-Cross 2022 " , " categoria " : " suv " , " preco " : 73224.0 , " budget_relaxed " : True } ,
{ " id " : 11 , " modelo " : " Toyota Corolla 2024 " , " categoria " : " suv " , " preco " : 76087.0 } ,
] ,
user_id = 5 ,
)
cached = state . get_entry ( " pending_stock_selections " , 5 )
self . assertIsNotNone ( cached )
self . assertEqual ( cached [ " payload " ] [ 0 ] [ " id " ] , 15 )
self . assertEqual ( cached [ " payload " ] [ 1 ] [ " modelo " ] , " Toyota Corolla 2024 " )
self . assertTrue ( cached [ " payload " ] [ 0 ] [ " budget_relaxed " ] )
self . assertFalse ( cached [ " payload " ] [ 1 ] [ " budget_relaxed " ] )
def test_entity_merge_can_enrich_message_plan_with_full_extraction ( self ) :
service = OrquestradorService . __new__ ( OrquestradorService )
service . normalizer = EntityNormalizer ( )
@ -747,6 +858,7 @@ class TurnDecisionContractTests(unittest.IsolatedAsyncioTestCase):
service = OrquestradorService . __new__ ( OrquestradorService )
service . state = state
service . normalizer = EntityNormalizer ( )
service . policy = ConversationPolicy ( service = service )
service . _empty_extraction_payload = service . normalizer . empty_extraction_payload
service . _log_turn_event = lambda * args , * * kwargs : None
service . _compose_order_aware_response = lambda response , user_id , queue_notice = None : response
@ -955,6 +1067,7 @@ class TurnDecisionContractTests(unittest.IsolatedAsyncioTestCase):
service = OrquestradorService . __new__ ( OrquestradorService )
service . state = state
service . normalizer = EntityNormalizer ( )
service . policy = ConversationPolicy ( service = service )
service . _empty_extraction_payload = service . normalizer . empty_extraction_payload
service . _log_turn_event = lambda * args , * * kwargs : None
service . _compose_order_aware_response = lambda response , user_id , queue_notice = None : response
@ -1080,92 +1193,701 @@ class TurnDecisionContractTests(unittest.IsolatedAsyncioTestCase):
self . assertIn ( " o modelo do veiculo " , response )
def test_should_prioritize_order_flow_when_cancel_draft_is_open ( self ) :
async def test_handle_message_skips_inventory_tool_and_uses_order_flow_for_list_selection_follow_up ( self ) :
state = FakeState (
entries = {
" pending_cancel_order_drafts " : {
contexts = {
1 : {
" payload " : { " numero_pedido " : " PED-202603101204814-6ED33A " } ,
" expires_at " : datetime . utcnow ( ) + timedelta ( minutes = 15 ) ,
}
" active_domain " : " sales " ,
" generic_memory " : { " orcamento_max " : 70000 , " perfil_veiculo " : [ " suv " ] } ,
" shared_memory " : { " orcamento_max " : 70000 , " perfil_veiculo " : [ " suv " ] } ,
" order_queue " : [ ] ,
" pending_order_selection " : None ,
" pending_switch " : None ,
" last_stock_results " : [
{ " id " : 15 , " modelo " : " Volkswagen T-Cross 2022 " , " categoria " : " suv " , " preco " : 73224.0 } ,
{ " id " : 11 , " modelo " : " Toyota Corolla 2024 " , " categoria " : " suv " , " preco " : 76087.0 } ,
] ,
" selected_vehicle " : None ,
}
}
)
service = OrquestradorService . __new__ ( OrquestradorService )
service . state = state
service . normalizer = EntityNormalizer ( )
service . policy = ConversationPolicy ( service = service )
service . _empty_extraction_payload = service . normalizer . empty_extraction_payload
service . _log_turn_event = lambda * args , * * kwargs : None
service . _compose_order_aware_response = lambda response , user_id , queue_notice = None : response
service . _get_user_context = lambda user_id : state . get_user_context ( user_id )
service . _save_user_context = lambda user_id , context : state . save_user_context ( user_id , context )
prioritized = service . _should_prioritize_order_flow (
turn_decision = { " intent " : " general " , " domain " : " general " , " action " : " answer_user " } ,
extracted_entities = {
async def fake_maybe_auto_advance_next_order ( base_response : str , user_id : int | None ) :
return base_response
service . _maybe_auto_advance_next_order = fake_maybe_auto_advance_next_order
service . _upsert_user_context = lambda user_id : None
async def fake_extract_turn_decision ( message : str , user_id : int | None ) :
return {
" intent " : " inventory_search " ,
" domain " : " sales " ,
" action " : " call_tool " ,
" entities " : {
" generic_memory " : { } ,
" review_fields " : { } ,
" review_management_fields " : { } ,
" order_fields " : { } ,
" cancel_order_fields " : { } ,
" intents " : { } ,
} ,
user_id = 1 ,
)
" missing_fields " : [ ] ,
" selection_index " : None ,
" tool_name " : " consultar_estoque " ,
" tool_arguments " : { " preco_max " : 70000 , " categoria " : " suv " } ,
" response_to_user " : " " ,
}
self . assertTrue ( prioritized )
service . _extract_turn_decision_with_llm = fake_extract_turn_decision
async def test_pending_order_selection_prefers_turn_decision_domain ( self ) :
state = FakeState (
contexts = {
9 : {
" pending_order_selection " : {
async def fake_try_handle_immediate_context_reset ( * * kwargs ) :
return None
service . _try_handle_immediate_context_reset = fake_try_handle_immediate_context_reset
async def fake_try_resolve_pending_order_selection ( * * kwargs ) :
return None
service . _try_resolve_pending_order_selection = fake_try_resolve_pending_order_selection
async def fake_try_continue_queued_order ( * * kwargs ) :
return None
service . _try_continue_queued_order = fake_try_continue_queued_order
async def fake_extract_message_plan ( message : str , user_id : int | None ) :
return {
" orders " : [
{ " domain " : " review " , " message " : " agendar revisao " , " memory_seed " : { } } ,
{ " domain " : " sales " , " message " : " fazer pedido " , " memory_seed " : { } } ,
] ,
" expires_at " : datetime . utcnow ( ) + timedelta ( minutes = 15 ) ,
} ,
" order_queue " : [ ] ,
" active_domain " : " general " ,
" generic_memory " : { } ,
{
" domain " : " sales " ,
" message " : message ,
" entities " : service . normalizer . empty_extraction_payload ( ) ,
}
]
}
)
policy = ConversationPolicy ( service = FakePolicyService ( state ) )
response = await policy . try_resolve_pending_order_selection (
message = " quero comprar " ,
user_id = 9 ,
turn_decision = { " domain " : " sales " , " intent " : " order_create " , " action " : " collect_order_create " } ,
service . _extract_message_plan_with_llm = fake_extract_message_plan
service . _prepare_message_for_single_order = lambda message , user_id , routing_plan = None : ( message , None , None )
service . _resolve_entities_for_message_plan = lambda message_plan , routed_message : service . normalizer . empty_extraction_payload ( )
async def fake_extract_entities ( message : str , user_id : int | None ) :
return {
" generic_memory " : { } ,
" review_fields " : { } ,
" review_management_fields " : { } ,
" order_fields " : { } ,
" cancel_order_fields " : { } ,
" intents " : { } ,
}
service . _extract_entities_with_llm = fake_extract_entities
async def fake_extract_missing_sales_search_context_with_llm ( * * kwargs ) :
return { }
service . _extract_missing_sales_search_context_with_llm = fake_extract_missing_sales_search_context_with_llm
service . _domain_from_intents = lambda intents : " general "
service . _handle_context_switch = lambda * * kwargs : None
service . _update_active_domain = lambda * * kwargs : None
async def fake_try_execute_orchestration_control_tool ( * * kwargs ) :
return None
service . _try_execute_orchestration_control_tool = fake_try_execute_orchestration_control_tool
async def fake_try_execute_business_tool_from_turn_decision ( * * kwargs ) :
return " nao deveria chamar tool "
service . _try_execute_business_tool_from_turn_decision = fake_try_execute_business_tool_from_turn_decision
async def fake_try_handle_review_management ( * * kwargs ) :
return None
service . _try_handle_review_management = fake_try_handle_review_management
async def fake_try_confirm_pending_review ( * * kwargs ) :
return None
service . _try_confirm_pending_review = fake_try_confirm_pending_review
async def fake_try_collect_and_schedule_review ( * * kwargs ) :
return None
service . _try_collect_and_schedule_review = fake_try_collect_and_schedule_review
async def fake_try_collect_and_cancel_order ( * * kwargs ) :
return None
service . _try_collect_and_cancel_order = fake_try_collect_and_cancel_order
async def fake_try_handle_order_listing ( * * kwargs ) :
return None
service . _try_handle_order_listing = fake_try_handle_order_listing
async def fake_try_collect_and_create_order ( * * kwargs ) :
return " Para realizar o pedido, preciso dos dados abaixo: \n - o CPF do cliente "
service . _try_collect_and_create_order = fake_try_collect_and_create_order
response = await service . handle_message (
" quero a opcao 1 " ,
user_id = 1 ,
)
self . assertIn ( " Vou comecar por: Venda: fazer pedido " , response )
self . assertIn ( " CPF do cliente " , response )
async def test_pending_order_selection_prefers_turn_decision_selection_index ( self ) :
async def test_ handle_message_short_circuits_llm_when_pending_stock_selection_matches_list_choice ( self ) :
state = FakeState (
contexts = {
9 : {
" pending_order_selection " : {
" orders " : [
{ " domain " : " review " , " message " : " agendar revisao " , " memory_seed " : { } } ,
{ " domain " : " sales " , " message " : " fazer pedido " , " memory_seed " : { } } ,
entrie s= {
" pending_stock_selections " : {
1 : {
" payload " : [
{ " id" : 15 , " modelo " : " Volkswagen T-Cross 2022 " , " categoria " : " suv " , " preco " : 73224.0 } ,
{ " id" : 11 , " modelo " : " Toyota Corolla 2024 " , " categoria " : " suv " , " preco " : 76087.0 } ,
] ,
" expires_at " : datetime . utcnow ( ) + timedelta ( minutes = 15 ) ,
}
}
} ,
contexts = {
1 : {
" active_domain " : " sales " ,
" generic_memory " : { " orcamento_max " : 70000 , " perfil_veiculo " : [ " suv " ] } ,
" shared_memory " : { " orcamento_max " : 70000 , " perfil_veiculo " : [ " suv " ] } ,
" order_queue " : [ ] ,
" active_domain " : " general " ,
" generic_memory " : { } ,
" pending_order_selection " : None ,
" pending_switch " : None ,
" last_stock_results " : [ ] ,
" selected_vehicle " : None ,
}
}
)
policy = ConversationPolicy ( service = FakePolicyService ( state ) )
service = OrquestradorService . __new__ ( OrquestradorService )
service . state = state
service . normalizer = EntityNormalizer ( )
service . policy = ConversationPolicy ( service = service )
service . _empty_extraction_payload = service . normalizer . empty_extraction_payload
service . _log_turn_event = lambda * args , * * kwargs : None
service . _compose_order_aware_response = lambda response , user_id , queue_notice = None : response
service . _get_user_context = lambda user_id : state . get_user_context ( user_id )
service . _save_user_context = lambda user_id , context : state . save_user_context ( user_id , context )
response = await policy . try_resolve_pending_order_selection (
message = " esse " ,
user_id = 9 ,
turn_decision = { " domain " : " general " , " intent " : " general " , " action " : " answer_user " , " selection_index " : 1 } ,
)
async def fake_maybe_auto_advance_next_order ( base_response : str , user_id : int | None ) :
return base_response
self . assertIn ( " Vou comecar por: Venda: fazer pedido " , response )
service . _maybe_auto_advance_next_order = fake_maybe_auto_advance_next_order
service . _upsert_user_context = lambda user_id : None
async def test_try_continue_queue_prefers_turn_decision_action ( self ) :
state = FakeState (
async def fake_extract_turn_decision ( message : str , user_id : int | None ) :
raise AssertionError ( " nao deveria consultar o LLM para selecao pendente de estoque " )
service . _extract_turn_decision_with_llm = fake_extract_turn_decision
async def fake_try_handle_immediate_context_reset ( * * kwargs ) :
return None
service . _try_handle_immediate_context_reset = fake_try_handle_immediate_context_reset
async def fake_try_resolve_pending_order_selection ( * * kwargs ) :
return None
service . _try_resolve_pending_order_selection = fake_try_resolve_pending_order_selection
async def fake_try_continue_queued_order ( * * kwargs ) :
return None
service . _try_continue_queued_order = fake_try_continue_queued_order
async def fake_try_collect_and_create_order ( * * kwargs ) :
return " Para realizar o pedido, preciso dos dados abaixo: \n - o CPF do cliente "
service . _try_collect_and_create_order = fake_try_collect_and_create_order
response = await service . handle_message (
" quero a opcao 1 " ,
user_id = 1 ,
)
self . assertIn ( " CPF do cliente " , response )
async def test_handle_message_keeps_sales_flow_when_cpf_follow_up_is_misclassified_as_review ( self ) :
state = FakeState (
entries = {
" pending_order_drafts " : {
1 : {
" payload " : { " vehicle_id " : 15 , " modelo_veiculo " : " Volkswagen T-Cross 2022 " , " valor_veiculo " : 73224.0 } ,
" expires_at " : datetime . utcnow ( ) + timedelta ( minutes = 15 ) ,
}
}
} ,
contexts = {
1 : {
" active_domain " : " sales " ,
" generic_memory " : { " orcamento_max " : 70000 , " perfil_veiculo " : [ " suv " ] } ,
" shared_memory " : { " orcamento_max " : 70000 , " perfil_veiculo " : [ " suv " ] } ,
" order_queue " : [ ] ,
" pending_order_selection " : None ,
" pending_switch " : None ,
" last_stock_results " : [
{ " id " : 15 , " modelo " : " Volkswagen T-Cross 2022 " , " categoria " : " suv " , " preco " : 73224.0 , " budget_relaxed " : True } ,
] ,
" selected_vehicle " : { " id " : 15 , " modelo " : " Volkswagen T-Cross 2022 " , " categoria " : " suv " , " preco " : 73224.0 , " budget_relaxed " : True } ,
}
}
)
service = OrquestradorService . __new__ ( OrquestradorService )
service . state = state
service . normalizer = EntityNormalizer ( )
service . _empty_extraction_payload = service . normalizer . empty_extraction_payload
service . _log_turn_event = lambda * args , * * kwargs : None
service . _compose_order_aware_response = lambda response , user_id , queue_notice = None : response
service . _get_user_context = lambda user_id : state . get_user_context ( user_id )
service . _save_user_context = lambda user_id , context : state . save_user_context ( user_id , context )
async def fake_maybe_auto_advance_next_order ( base_response : str , user_id : int | None ) :
return base_response
service . _maybe_auto_advance_next_order = fake_maybe_auto_advance_next_order
service . _upsert_user_context = lambda user_id : None
async def fake_extract_turn_decision ( message : str , user_id : int | None ) :
raise AssertionError ( " nao deveria consultar o LLM para continuar um fluxo de venda aberto " )
service . _extract_turn_decision_with_llm = fake_extract_turn_decision
async def fake_try_handle_immediate_context_reset ( * * kwargs ) :
return None
service . _try_handle_immediate_context_reset = fake_try_handle_immediate_context_reset
async def fake_try_resolve_pending_order_selection ( * * kwargs ) :
return None
service . _try_resolve_pending_order_selection = fake_try_resolve_pending_order_selection
async def fake_try_continue_queued_order ( * * kwargs ) :
return None
service . _try_continue_queued_order = fake_try_continue_queued_order
async def fake_extract_message_plan ( message : str , user_id : int | None ) :
return {
" orders " : [
{
" domain " : " sales " ,
" message " : message ,
" entities " : service . normalizer . empty_extraction_payload ( ) ,
}
]
}
service . _extract_message_plan_with_llm = fake_extract_message_plan
service . _prepare_message_for_single_order = lambda message , user_id , routing_plan = None : ( message , None , None )
service . _resolve_entities_for_message_plan = lambda message_plan , routed_message : service . normalizer . empty_extraction_payload ( )
async def fake_extract_entities ( message : str , user_id : int | None ) :
return {
" generic_memory " : { } ,
" review_fields " : { } ,
" review_management_fields " : { } ,
" order_fields " : { } ,
" cancel_order_fields " : { } ,
" intents " : { } ,
}
service . _extract_entities_with_llm = fake_extract_entities
async def fake_extract_missing_sales_search_context_with_llm ( * * kwargs ) :
return { }
service . _extract_missing_sales_search_context_with_llm = fake_extract_missing_sales_search_context_with_llm
service . _domain_from_intents = lambda intents : " general "
service . _update_active_domain = lambda * * kwargs : None
async def fake_try_execute_orchestration_control_tool ( * * kwargs ) :
return None
service . _try_execute_orchestration_control_tool = fake_try_execute_orchestration_control_tool
async def fake_try_execute_business_tool_from_turn_decision ( * * kwargs ) :
return " nao deveria executar tool planejada "
service . _try_execute_business_tool_from_turn_decision = fake_try_execute_business_tool_from_turn_decision
async def fake_try_handle_review_management ( * * kwargs ) :
return None
service . _try_handle_review_management = fake_try_handle_review_management
async def fake_try_confirm_pending_review ( * * kwargs ) :
return None
service . _try_confirm_pending_review = fake_try_confirm_pending_review
async def fake_try_collect_and_schedule_review ( * * kwargs ) :
return None
service . _try_collect_and_schedule_review = fake_try_collect_and_schedule_review
async def fake_try_collect_and_cancel_order ( * * kwargs ) :
return None
service . _try_collect_and_cancel_order = fake_try_collect_and_cancel_order
async def fake_try_handle_order_listing ( * * kwargs ) :
return None
service . _try_handle_order_listing = fake_try_handle_order_listing
async def fake_try_collect_and_create_order ( * * kwargs ) :
return " Pedido criado com sucesso. "
service . _try_collect_and_create_order = fake_try_collect_and_create_order
response = await service . handle_message (
" 12345678909 " ,
user_id = 1 ,
)
self . assertEqual ( response , " Pedido criado com sucesso. " )
async def test_handle_message_prioritizes_immediate_reset_before_active_sales_follow_up ( self ) :
state = FakeState (
contexts = {
1 : {
" active_domain " : " sales " ,
" order_queue " : [ ] ,
" pending_order_selection " : None ,
" pending_switch " : None ,
" last_stock_results " : [ ] ,
" selected_vehicle " : None ,
}
}
)
service = OrquestradorService . __new__ ( OrquestradorService )
service . state = state
service . normalizer = EntityNormalizer ( )
service . policy = ConversationPolicy ( service = service )
service . _empty_extraction_payload = service . normalizer . empty_extraction_payload
service . _log_turn_event = lambda * args , * * kwargs : None
service . _compose_order_aware_response = lambda response , user_id , queue_notice = None : response
service . _get_user_context = lambda user_id : state . get_user_context ( user_id )
service . _save_user_context = lambda user_id , context : state . save_user_context ( user_id , context )
async def fake_maybe_auto_advance_next_order ( base_response : str , user_id : int | None ) :
return base_response
service . _maybe_auto_advance_next_order = fake_maybe_auto_advance_next_order
service . _upsert_user_context = lambda user_id : None
service . _is_order_selection_reset_message = lambda message : True
async def fake_try_handle_immediate_context_reset ( * * kwargs ) :
return " Contexto da conversa limpo. Podemos recomecar do zero. "
service . _try_handle_immediate_context_reset = fake_try_handle_immediate_context_reset
async def fake_try_handle_pending_stock_selection_follow_up ( * * kwargs ) :
raise AssertionError ( " nao deveria entrar no follow-up de estoque antes do reset " )
service . _try_handle_pending_stock_selection_follow_up = fake_try_handle_pending_stock_selection_follow_up
async def fake_try_handle_active_sales_follow_up ( * * kwargs ) :
raise AssertionError ( " nao deveria entrar no follow-up de vendas antes do reset " )
service . _try_handle_active_sales_follow_up = fake_try_handle_active_sales_follow_up
response = await service . handle_message (
" esqueca tudo e vamos recomecar " ,
user_id = 1 ,
)
self . assertEqual ( response , " Contexto da conversa limpo. Podemos recomecar do zero. " )
async def test_try_handle_immediate_context_reset_treats_vamos_recomecar_suffix_as_pure_reset ( self ) :
state = FakeState (
contexts = {
1 : {
" active_domain " : " sales " ,
" generic_memory " : { " orcamento_max " : 65000 } ,
" shared_memory " : { " orcamento_max " : 65000 } ,
" order_queue " : [ ] ,
" pending_order_selection " : None ,
" pending_switch " : None ,
" last_stock_results " : [ { " id " : 7 , " modelo " : " Fiat Argo 2020 " , " categoria " : " suv " , " preco " : 61857.0 } ] ,
" selected_vehicle " : { " id " : 7 , " modelo " : " Fiat Argo 2020 " , " categoria " : " suv " , " preco " : 61857.0 } ,
}
}
)
service = OrquestradorService . __new__ ( OrquestradorService )
service . state = state
service . normalizer = EntityNormalizer ( )
service . policy = ConversationPolicy ( service = service )
service . _get_user_context = lambda user_id : state . get_user_context ( user_id )
service . _save_user_context = lambda user_id , context : state . save_user_context ( user_id , context )
async def finish ( response : str , queue_notice : str | None = None ) :
return response
async def fake_handle_message ( message : str , user_id : int | None = None ) :
raise AssertionError ( " nao deveria reprocessar ' vamos recomecar ' como mensagem de negocio " )
service . handle_message = fake_handle_message
response = await service . _try_handle_immediate_context_reset (
message = " esqueca tudo e vamos recomecar " ,
user_id = 1 ,
turn_decision = { " action " : " clear_context " } ,
finish = finish ,
)
self . assertEqual ( response , " Contexto da conversa limpo. Podemos recomecar do zero. " )
self . assertEqual ( state . get_user_context ( 1 ) [ " active_domain " ] , " general " )
self . assertEqual ( state . get_user_context ( 1 ) [ " generic_memory " ] , { } )
async def test_active_sales_follow_up_allows_new_budget_search_to_reset_open_order_draft ( self ) :
state = FakeState (
entries = {
" pending_order_drafts " : {
1 : {
" payload " : {
" cpf " : " 12345678909 " ,
" vehicle_id " : 15 ,
" modelo_veiculo " : " Volkswagen T-Cross 2022 " ,
" valor_veiculo " : 73224.0 ,
} ,
" expires_at " : datetime . utcnow ( ) + timedelta ( minutes = 15 ) ,
}
}
} ,
contexts = {
1 : {
" active_domain " : " sales " ,
" generic_memory " : { " cpf " : " 12345678909 " , " orcamento_max " : 70000 , " perfil_veiculo " : [ " suv " ] } ,
" shared_memory " : { " cpf " : " 12345678909 " , " orcamento_max " : 70000 , " perfil_veiculo " : [ " suv " ] } ,
" order_queue " : [ ] ,
" pending_order_selection " : None ,
" pending_switch " : None ,
" last_stock_results " : [
{ " id " : 15 , " modelo " : " Volkswagen T-Cross 2022 " , " categoria " : " suv " , " preco " : 73224.0 , " budget_relaxed " : True } ,
] ,
" selected_vehicle " : { " id " : 15 , " modelo " : " Volkswagen T-Cross 2022 " , " categoria " : " suv " , " preco " : 73224.0 , " budget_relaxed " : True } ,
}
}
)
service = OrquestradorService . __new__ ( OrquestradorService )
service . state = state
service . normalizer = EntityNormalizer ( )
service . _get_user_context = lambda user_id : state . get_user_context ( user_id )
service . _save_user_context = lambda user_id , context : state . save_user_context ( user_id , context )
async def fake_try_collect_and_create_order ( * * kwargs ) :
raise AssertionError ( " nao deveria consumir nova busca de estoque no atalho de venda ativa " )
service . _try_collect_and_create_order = fake_try_collect_and_create_order
async def finish ( response : str , queue_notice : str | None = None ) :
return response
response = await service . _try_handle_active_sales_follow_up (
message = " agora quero comprar um carro de ate 60 mil " ,
user_id = 1 ,
finish = finish ,
)
self . assertIsNone ( response )
self . assertIsNone ( state . get_entry ( " pending_order_drafts " , 1 ) )
self . assertIsNone ( state . get_user_context ( 1 ) [ " selected_vehicle " ] )
async def test_orchestration_control_ignores_cancel_flow_tool_for_fresh_sales_request_without_open_flow ( self ) :
state = FakeState (
contexts = {
1 : {
" active_domain " : " sales " ,
" order_queue " : [ ] ,
" pending_order_selection " : None ,
" pending_switch " : None ,
" last_stock_results " : [ ] ,
" selected_vehicle " : None ,
}
}
)
service = OrquestradorService . __new__ ( OrquestradorService )
service . state = state
service . normalizer = EntityNormalizer ( )
service . policy = ConversationPolicy ( service = service )
class DummyRegistry :
def get_tools ( self ) :
return [ ]
class DummyExecutor :
async def execute ( self , tool_name , arguments , user_id = None ) :
raise AssertionError ( " nao deveria executar cancelar_fluxo_atual para nova busca operacional " )
service . registry = DummyRegistry ( )
service . tool_executor = DummyExecutor ( )
service . _get_user_context = lambda user_id : state . get_user_context ( user_id )
service . _save_user_context = lambda user_id , context : state . save_user_context ( user_id , context )
service . _has_open_flow = lambda user_id , domain : service . policy . has_open_flow ( user_id = user_id , domain = domain )
service . _is_low_value_response = lambda text : False
async def fake_call_llm_with_trace ( * * kwargs ) :
return {
" response " : " " ,
" tool_call " : {
" name " : " cancelar_fluxo_atual " ,
" arguments " : { " motivo " : " cliente mudou de ideia e iniciou nova busca " } ,
} ,
}
service . _call_llm_with_trace = fake_call_llm_with_trace
async def finish ( response : str , queue_notice : str | None = None ) :
return response
response = await service . _try_execute_orchestration_control_tool (
message = " agora eu quero comprar um carro de ate 70 mil " ,
user_id = 1 ,
turn_decision = { } ,
extracted_entities = { } ,
queue_notice = None ,
finish = finish ,
)
self . assertIsNone ( response )
def test_should_prioritize_order_flow_when_cancel_draft_is_open ( self ) :
state = FakeState (
entries = {
" pending_cancel_order_drafts " : {
1 : {
" payload " : { " numero_pedido " : " PED-202603101204814-6ED33A " } ,
" expires_at " : datetime . utcnow ( ) + timedelta ( minutes = 15 ) ,
}
}
}
)
service = OrquestradorService . __new__ ( OrquestradorService )
service . state = state
service . normalizer = EntityNormalizer ( )
prioritized = service . _should_prioritize_order_flow (
turn_decision = { " intent " : " general " , " domain " : " general " , " action " : " answer_user " } ,
extracted_entities = {
" generic_memory " : { } ,
" review_fields " : { } ,
" review_management_fields " : { } ,
" order_fields " : { } ,
" cancel_order_fields " : { } ,
" intents " : { } ,
} ,
user_id = 1 ,
)
self . assertTrue ( prioritized )
def test_should_prioritize_order_flow_when_message_selects_vehicle_from_last_stock_results ( self ) :
state = FakeState (
contexts = {
1 : {
" active_domain " : " sales " ,
" generic_memory " : { " orcamento_max " : 70000 , " perfil_veiculo " : [ " suv " ] } ,
" shared_memory " : { " orcamento_max " : 70000 , " perfil_veiculo " : [ " suv " ] } ,
" last_stock_results " : [
{ " id " : 15 , " modelo " : " Volkswagen T-Cross 2022 " , " categoria " : " suv " , " preco " : 73224.0 } ,
{ " id " : 11 , " modelo " : " Toyota Corolla 2024 " , " categoria " : " suv " , " preco " : 76087.0 } ,
] ,
" selected_vehicle " : None ,
}
}
)
service = OrquestradorService . __new__ ( OrquestradorService )
service . state = state
service . normalizer = EntityNormalizer ( )
service . _get_user_context = lambda user_id : state . get_user_context ( user_id )
prioritized = service . _should_prioritize_order_flow (
turn_decision = { " intent " : " inventory_search " , " domain " : " sales " , " action " : " call_tool " } ,
extracted_entities = {
" generic_memory " : { } ,
" review_fields " : { } ,
" review_management_fields " : { } ,
" order_fields " : { } ,
" cancel_order_fields " : { } ,
" intents " : { } ,
} ,
user_id = 1 ,
message = " quero a opcao 1 " ,
)
self . assertTrue ( prioritized )
async def test_pending_order_selection_prefers_turn_decision_domain ( self ) :
state = FakeState (
contexts = {
9 : {
" pending_order_selection " : {
" orders " : [
{ " domain " : " review " , " message " : " agendar revisao " , " memory_seed " : { } } ,
{ " domain " : " sales " , " message " : " fazer pedido " , " memory_seed " : { } } ,
] ,
" expires_at " : datetime . utcnow ( ) + timedelta ( minutes = 15 ) ,
} ,
" order_queue " : [ ] ,
" active_domain " : " general " ,
" generic_memory " : { } ,
}
}
)
policy = ConversationPolicy ( service = FakePolicyService ( state ) )
response = await policy . try_resolve_pending_order_selection (
message = " quero comprar " ,
user_id = 9 ,
turn_decision = { " domain " : " sales " , " intent " : " order_create " , " action " : " collect_order_create " } ,
)
self . assertIn ( " Vou comecar por: Venda: fazer pedido " , response )
async def test_pending_order_selection_prefers_turn_decision_selection_index ( self ) :
state = FakeState (
contexts = {
9 : {
" pending_order_selection " : {
" orders " : [
{ " domain " : " review " , " message " : " agendar revisao " , " memory_seed " : { } } ,
{ " domain " : " sales " , " message " : " fazer pedido " , " memory_seed " : { } } ,
] ,
" expires_at " : datetime . utcnow ( ) + timedelta ( minutes = 15 ) ,
} ,
" order_queue " : [ ] ,
" active_domain " : " general " ,
" generic_memory " : { } ,
}
}
)
policy = ConversationPolicy ( service = FakePolicyService ( state ) )
response = await policy . try_resolve_pending_order_selection (
message = " esse " ,
user_id = 9 ,
turn_decision = { " domain " : " general " , " intent " : " general " , " action " : " answer_user " , " selection_index " : 1 } ,
)
self . assertIn ( " Vou comecar por: Venda: fazer pedido " , response )
async def test_try_continue_queue_prefers_turn_decision_action ( self ) :
state = FakeState (
contexts = {
9 : {
" pending_switch " : {
@ -1221,7 +1943,11 @@ class TurnDecisionContractTests(unittest.IsolatedAsyncioTestCase):
turn_decision = { " domain " : " review " , " intent " : " review_schedule " , " action " : " collect_review_schedule " } ,
)
self . assertEqual ( response , " Certo, contexto anterior encerrado. Vamos seguir com agendamento de revisao. " )
self . assertEqual (
response ,
" Certo, contexto anterior encerrado. Vamos seguir com agendamento de revisao. \n "
" Pode me informar a placa ou, se preferir, ja mandar placa, data/hora, modelo, ano, km e se ja fez revisao. " ,
)
def test_prepare_message_for_single_order_defers_explicit_domain_switch_with_open_flow ( self ) :
state = FakeState (