130 lines
4.1 KiB
Python
130 lines
4.1 KiB
Python
"""Dynamic instruction provider for VAia agent."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import TYPE_CHECKING
|
|
|
|
if TYPE_CHECKING:
|
|
from google.adk.agents.readonly_context import ReadonlyContext
|
|
|
|
from va_agent.notifications import NotificationService
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
async def provide_dynamic_instruction(
|
|
notification_service: NotificationService,
|
|
ctx: ReadonlyContext | None = None,
|
|
) -> str:
|
|
"""Provide dynamic instructions based on pending notifications.
|
|
|
|
This function is called by the ADK agent on each message. It:
|
|
1. Checks if this is the first message in the session (< 2 events)
|
|
2. Queries Firestore for pending notifications
|
|
3. Marks them as notified
|
|
4. Returns a dynamic instruction for the agent to mention them
|
|
|
|
Args:
|
|
notification_service: Service for fetching/marking notifications
|
|
ctx: Agent context containing session information
|
|
|
|
Returns:
|
|
Dynamic instruction string (empty if no notifications or not first message)
|
|
|
|
"""
|
|
# Only check notifications on the first message
|
|
if not ctx:
|
|
logger.debug("No context available for dynamic instruction")
|
|
return ""
|
|
|
|
session = ctx.session
|
|
if not session:
|
|
logger.debug("No session available for dynamic instruction")
|
|
return ""
|
|
|
|
# FOR TESTING: Always check for notifications
|
|
# (comment out to enable first-message-only)
|
|
# Only check on first message (when events list is empty
|
|
# or has only 1-2 events)
|
|
# Events include both user and agent messages, so < 2 means first interaction
|
|
# event_count = len(session.events) if session.events else 0
|
|
#
|
|
# if event_count >= 2:
|
|
# logger.debug(
|
|
# "Skipping notification check: not first message (event_count=%d)",
|
|
# event_count,
|
|
# )
|
|
# return ""
|
|
|
|
# Extract phone number from user_id (they are the same in this implementation)
|
|
phone_number = session.user_id
|
|
|
|
logger.info(
|
|
"First message detected for user %s, checking for pending notifications",
|
|
phone_number,
|
|
)
|
|
|
|
try:
|
|
# Fetch pending notifications
|
|
pending_notifications = await notification_service.get_pending_notifications(
|
|
phone_number
|
|
)
|
|
|
|
if not pending_notifications:
|
|
logger.info("No pending notifications for user %s", phone_number)
|
|
return ""
|
|
|
|
# Build dynamic instruction with notification details
|
|
notification_ids = [
|
|
nid
|
|
for n in pending_notifications
|
|
if (nid := n.get("id_notificacion")) is not None
|
|
]
|
|
count = len(pending_notifications)
|
|
|
|
# Format notification details for the agent
|
|
notification_details = []
|
|
for notif in pending_notifications:
|
|
evento = notif.get("nombre_evento_dialogflow", "notificacion")
|
|
texto = notif.get("texto", "Sin texto")
|
|
notification_details.append(f" - Evento: {evento} | Texto: {texto}")
|
|
|
|
details_text = "\n".join(notification_details)
|
|
|
|
instruction = f"""
|
|
IMPORTANTE - NOTIFICACIONES PENDIENTES:
|
|
|
|
El usuario tiene {count} notificación(es) sin leer:
|
|
|
|
{details_text}
|
|
|
|
INSTRUCCIONES:
|
|
- Menciona estas notificaciones de forma natural en tu respuesta inicial
|
|
- No necesitas leerlas todas literalmente, solo hazle saber que las tiene
|
|
- Sé breve y directo según tu personalidad (directo y cálido)
|
|
- Si el usuario pregunta algo específico, prioriza responder eso primero\
|
|
y luego menciona las notificaciones
|
|
|
|
Ejemplo: "¡Hola! 👋 Tienes {count} notificación(es)\
|
|
pendiente(s). ¿Te gustaría revisarlas?"
|
|
"""
|
|
|
|
# Mark notifications as notified in Firestore
|
|
await notification_service.mark_as_notified(phone_number, notification_ids)
|
|
|
|
logger.info(
|
|
"Returning dynamic instruction with %d notification(s) for user %s",
|
|
count,
|
|
phone_number,
|
|
)
|
|
|
|
except Exception:
|
|
logger.exception(
|
|
"Error building dynamic instruction for user %s",
|
|
phone_number,
|
|
)
|
|
return ""
|
|
else:
|
|
return instruction
|