From aa816f0d64872929682eeac14f95dcb26066b42f Mon Sep 17 00:00:00 2001 From: Anibal Angulo Date: Fri, 20 Feb 2026 14:30:45 +0000 Subject: [PATCH] Fix critical bugs --- pyproject.toml | 2 +- src/capa_de_integracion/services/conversation.py | 5 +++-- src/capa_de_integracion/services/notifications.py | 9 +++++++-- src/capa_de_integracion/services/quick_reply/session.py | 7 +++++-- tests/services/test_quick_reply_session.py | 5 +++++ 5 files changed, 21 insertions(+), 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 4bcd026..ff867fe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,7 +73,7 @@ filterwarnings = [ ] env = [ - "FIRESTORE_EMULATOR_HOST=[::1]:8911", + "FIRESTORE_EMULATOR_HOST=[::1]:8469", "GCP_PROJECT_ID=test-project", "GCP_LOCATION=us-central1", "GCP_FIRESTORE_DATABASE_ID=(default)", diff --git a/src/capa_de_integracion/services/conversation.py b/src/capa_de_integracion/services/conversation.py index 007fec4..cc508af 100644 --- a/src/capa_de_integracion/services/conversation.py +++ b/src/capa_de_integracion/services/conversation.py @@ -370,12 +370,13 @@ class ConversationManagerService: logger.info("Matched quick reply: %s", pregunta.titulo) break - # If no match, use first question as default or delegate to normal flow + # If no match, delegate to normal flow if not matched_answer: logger.warning( - "No matching quick reply found for message: '%s'.", + "No matching quick reply found for message: '%s'. Falling back to RAG.", request.mensaje, ) + return None # Create response with the matched quick reply answer return DetectIntentResponse( diff --git a/src/capa_de_integracion/services/notifications.py b/src/capa_de_integracion/services/notifications.py index 6bbee00..d3652fe 100644 --- a/src/capa_de_integracion/services/notifications.py +++ b/src/capa_de_integracion/services/notifications.py @@ -17,6 +17,9 @@ logger = logging.getLogger(__name__) PREFIX_PO_PARAM = "notification_po_" +# Keep references to background tasks to prevent garbage collection +_background_tasks: set[asyncio.Task] = set() + class NotificationManagerService: """Manages notification processing and integration with conversations. @@ -127,6 +130,8 @@ class NotificationManagerService: ) # Fire and forget - don't await - _task = asyncio.create_task(save_notification_to_firestore()) + task = asyncio.create_task(save_notification_to_firestore()) # Store reference to prevent premature garbage collection - del _task + _background_tasks.add(task) + # Remove from set when done to prevent memory leak + task.add_done_callback(_background_tasks.discard) diff --git a/src/capa_de_integracion/services/quick_reply/session.py b/src/capa_de_integracion/services/quick_reply/session.py index fa6c1e9..7a8c01c 100644 --- a/src/capa_de_integracion/services/quick_reply/session.py +++ b/src/capa_de_integracion/services/quick_reply/session.py @@ -87,8 +87,11 @@ class QuickReplySessionService: """ self._validate_phone(telefono) - # Get or create session - session = await self.firestore_service.get_session_by_phone(telefono) + # Get or create session (check Redis first for consistency) + session = await self.redis_service.get_session(telefono) + if not session: + session = await self.firestore_service.get_session_by_phone(telefono) + if session: session_id = session.session_id await self.firestore_service.update_pantalla_contexto( diff --git a/tests/services/test_quick_reply_session.py b/tests/services/test_quick_reply_session.py index cea8172..10a272e 100644 --- a/tests/services/test_quick_reply_session.py +++ b/tests/services/test_quick_reply_session.py @@ -17,6 +17,7 @@ from capa_de_integracion.services.storage.redis import RedisService def mock_redis(): """Create mock Redis service.""" redis = Mock(spec=RedisService) + redis.get_session = AsyncMock() redis.save_session = AsyncMock() return redis @@ -67,6 +68,7 @@ async def test_validate_phone_whitespace(service): async def test_start_session_new_user(service, mock_firestore, mock_redis, mock_content): """Test starting a quick reply session for a new user.""" # Setup mocks + mock_redis.get_session.return_value = None # No session in Redis mock_firestore.get_session_by_phone.return_value = None # No existing session # Mock create_session to return a session with the ID that was passed in @@ -100,6 +102,7 @@ async def test_start_session_new_user(service, mock_firestore, mock_redis, mock_ assert result.session_id is not None # Session ID should be generated assert result.quick_replies.header == "Home Screen" + mock_redis.get_session.assert_called_once_with("555-1234") mock_firestore.get_session_by_phone.assert_called_once_with("555-1234") mock_firestore.create_session.assert_called_once() @@ -123,6 +126,7 @@ async def test_start_session_existing_user(service, mock_firestore, mock_redis, telefono="555-1234", pantalla_contexto="old_screen", ) + mock_redis.get_session.return_value = None # Not in Redis cache mock_firestore.get_session_by_phone.return_value = test_session test_quick_replies = QuickReplyScreen( @@ -145,6 +149,7 @@ async def test_start_session_existing_user(service, mock_firestore, mock_redis, assert result.session_id == test_session_id assert result.quick_replies.header == "Payments Screen" + mock_redis.get_session.assert_called_once_with("555-1234") mock_firestore.get_session_by_phone.assert_called_once_with("555-1234") mock_firestore.update_pantalla_contexto.assert_called_once_with( test_session_id,