From 6537808963d21753ab2727fee9507131bb830ee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vitor=20Hugo=20Belorio=20Sim=C3=A3o?= Date: Mon, 9 Mar 2026 18:21:09 -0300 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(flows):=20endurecer=20valida?= =?UTF-8?q?cao=20e=20preservar=20drafts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Impede que consultas de estoque caiam no fluxo de compra, evita reaproveitamento implícito de CPF para criar pedidos e mantém drafts quando a execução da tool falha. Também preserva corretamente o estado de revisão, limpando o draft apenas quando a operação conclui com sucesso ou quando a confirmação pendente passa a ser o estado principal. --- app/services/flows/order_flow.py | 30 +++++++++++++++++++++--------- app/services/flows/review_flow.py | 11 +++++------ 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/app/services/flows/order_flow.py b/app/services/flows/order_flow.py index cfad758..7d3e19b 100644 --- a/app/services/flows/order_flow.py +++ b/app/services/flows/order_flow.py @@ -15,6 +15,21 @@ from app.services.user.mock_customer_service import hydrate_mock_customer_from_c class OrderFlowMixin: + def _has_explicit_order_request(self, message: str) -> bool: + normalized = self._normalize_text(message).strip() + order_terms = { + "comprar", + "compra", + "pedido", + "pedir", + "financiar", + "financiamento", + "simular compra", + "realizar pedido", + "fazer um pedido", + } + return any(term in normalized for term in order_terms) + def _is_valid_cpf(self, cpf: str) -> bool: digits = re.sub(r"\D", "", cpf or "") if len(digits) != 11: @@ -102,6 +117,7 @@ class OrderFlowMixin: extracted = self._normalize_order_fields(extracted_fields) has_intent = normalized_intents.get("order_create", False) + explicit_order_request = self._has_explicit_order_request(message) if ( draft @@ -118,7 +134,7 @@ class OrderFlowMixin: self.state.pop_entry("pending_order_drafts", user_id) return None - if not has_intent and draft is None: + if draft is None and not (has_intent and explicit_order_request): return None if draft is None: @@ -128,15 +144,13 @@ class OrderFlowMixin: } draft["payload"].update(extracted) - self._try_prefill_order_cpf_from_memory(user_id=user_id, payload=draft["payload"]) - self._try_prefill_order_cpf_from_user_profile(user_id=user_id, payload=draft["payload"]) self._try_prefill_order_value_from_memory(user_id=user_id, payload=draft["payload"]) cpf_value = draft["payload"].get("cpf") if cpf_value and not self._is_valid_cpf(str(cpf_value)): draft["payload"].pop("cpf", None) self.state.set_entry("pending_order_drafts", user_id, draft) - return "Para seguir com o pedido, preciso de um CPF valido com 11 digitos." + return "Para seguir com o pedido, preciso de um CPF valido. Pode me informar novamente?" if cpf_value: try: await hydrate_mock_customer_from_cpf( @@ -146,7 +160,7 @@ class OrderFlowMixin: except ValueError: draft["payload"].pop("cpf", None) self.state.set_entry("pending_order_drafts", user_id, draft) - return "Para seguir com o pedido, preciso de um CPF valido com 11 digitos." + return "Para seguir com o pedido, preciso de um CPF valido. Pode me informar novamente?" valor = draft["payload"].get("valor_veiculo") if valor is not None: @@ -174,8 +188,7 @@ class OrderFlowMixin: ) except HTTPException as exc: return self._http_exception_detail(exc) - finally: - self.state.pop_entry("pending_order_drafts", user_id) + self.state.pop_entry("pending_order_drafts", user_id) return self._fallback_format_tool_result("realizar_pedido", tool_result) @@ -244,7 +257,6 @@ class OrderFlowMixin: ) except HTTPException as exc: return self._http_exception_detail(exc) - finally: - self.state.pop_entry("pending_cancel_order_drafts", user_id) + self.state.pop_entry("pending_cancel_order_drafts", user_id) return self._fallback_format_tool_result("cancelar_pedido", tool_result) diff --git a/app/services/flows/review_flow.py b/app/services/flows/review_flow.py index a042205..42ae0e4 100644 --- a/app/services/flows/review_flow.py +++ b/app/services/flows/review_flow.py @@ -91,8 +91,7 @@ class ReviewFlowMixin: ) except HTTPException as exc: return self._http_exception_detail(exc) - finally: - self.state.pop_entry("pending_review_management_drafts", user_id) + self.state.pop_entry("pending_review_management_drafts", user_id) return self._fallback_format_tool_result("editar_data_revisao", tool_result) missing = [field for field in ("protocolo",) if field not in draft["payload"]] @@ -109,8 +108,7 @@ class ReviewFlowMixin: ) except HTTPException as exc: return self._http_exception_detail(exc) - finally: - self.state.pop_entry("pending_review_management_drafts", user_id) + self.state.pop_entry("pending_review_management_drafts", user_id) return self._fallback_format_tool_result("cancelar_agendamento_revisao", tool_result) def _render_missing_review_fields_prompt(self, missing_fields: list[str]) -> str: @@ -295,9 +293,10 @@ class ReviewFlowMixin: exc=exc, user_id=user_id, ) + if self.state.get_entry("pending_review_confirmations", user_id, expire=True): + self.state.pop_entry("pending_review_drafts", user_id) return self._http_exception_detail(exc) - finally: - self.state.pop_entry("pending_review_drafts", user_id) + self.state.pop_entry("pending_review_drafts", user_id) self._store_last_review_package(user_id=user_id, payload=draft["payload"]) return self._fallback_format_tool_result("agendar_revisao", tool_result)