victor34593993 commited on
Commit
b6a216d
·
verified ·
1 Parent(s): 61b5750

events retention + recommendation

Browse files
app/prompts.py CHANGED
@@ -25,6 +25,7 @@ Reglas de uso:
25
  ## Vender, no solo informar
26
  - Cuando enseñes productos, usa `search_products`: las fichas se muestran solas como **tarjetas con foto, precio y botón de añadir** (no hace falta que las describas con tablas).
27
  - Si el cliente quiere comprar ("me llevo este", "quiero los dos", "ponme 2"), llama a `create_cart_link` con los variant_id y cantidades, y dale el **enlace para finalizar la compra**.
 
28
  - **Comparar:** si duda entre 2-4 productos, compáralos según SU necesidad (úsalos de search_products + fichas de search_knowledge) y recomiéndale uno con un motivo claro; ofrece añadirlo al carrito.
29
  - **Talla/compatibilidad:** ante "¿me vale?", "¿qué talla?", "¿es compatible con X?", busca la guía de tallas/compatibilidad en `search_knowledge`, haz 1-2 preguntas si hace falta, y responde con la fuente. Si no está el dato, dilo (no adivines tallas).
30
  - **Resolver objeciones:** si el cliente duda ("es caro", "no llega a tiempo", "¿y si lo devuelvo?"), responde PRIMERO con datos reales (plazo de envío, política de devoluciones de la base de conocimiento) antes de escalar; muchas veces es lo único que necesita para comprar.
 
25
  ## Vender, no solo informar
26
  - Cuando enseñes productos, usa `search_products`: las fichas se muestran solas como **tarjetas con foto, precio y botón de añadir** (no hace falta que las describas con tablas).
27
  - Si el cliente quiere comprar ("me llevo este", "quiero los dos", "ponme 2"), llama a `create_cart_link` con los variant_id y cantidades, y dale el **enlace para finalizar la compra**.
28
+ - **Recomendar / descubrir por intención:** si el cliente describe una NECESIDAD ("algo para vaciar la piscina", "lo más barato para regar", "un kit para empezar") o un PRESUPUESTO ("menos de 30€"), busca con `search_products` usando palabras clave de su necesidad, mira los precios/stock reales y recomiéndale 1-3 opciones que de verdad encajen, explicando por qué y respetando su presupuesto. Si hace falta, pregunta 1 detalle. Nunca recomiendes algo que no esté en los resultados.
29
  - **Comparar:** si duda entre 2-4 productos, compáralos según SU necesidad (úsalos de search_products + fichas de search_knowledge) y recomiéndale uno con un motivo claro; ofrece añadirlo al carrito.
30
  - **Talla/compatibilidad:** ante "¿me vale?", "¿qué talla?", "¿es compatible con X?", busca la guía de tallas/compatibilidad en `search_knowledge`, haz 1-2 preguntas si hace falta, y responde con la fuente. Si no está el dato, dilo (no adivines tallas).
31
  - **Resolver objeciones:** si el cliente duda ("es caro", "no llega a tiempo", "¿y si lo devuelvo?"), responde PRIMERO con datos reales (plazo de envío, política de devoluciones de la base de conocimiento) antes de escalar; muchas veces es lo único que necesita para comprar.
app/retention.py CHANGED
@@ -7,7 +7,7 @@ from datetime import UTC, datetime, timedelta
7
  from sqlalchemy import delete, select
8
  from sqlalchemy.ext.asyncio import AsyncSession
9
 
10
- from app.models import ChatMessage, ChatSession
11
 
12
 
13
  async def purge_old_sessions(db: AsyncSession, days: int) -> int:
@@ -15,7 +15,10 @@ async def purge_old_sessions(db: AsyncSession, days: int) -> int:
15
  ids = (
16
  await db.execute(select(ChatSession.id).where(ChatSession.last_seen < cutoff))
17
  ).scalars().all()
 
 
18
  if not ids:
 
19
  return 0
20
  await db.execute(delete(ChatMessage).where(ChatMessage.session_id.in_(ids)))
21
  await db.execute(delete(ChatSession).where(ChatSession.id.in_(ids)))
 
7
  from sqlalchemy import delete, select
8
  from sqlalchemy.ext.asyncio import AsyncSession
9
 
10
+ from app.models import ChatMessage, ChatSession, Event
11
 
12
 
13
  async def purge_old_sessions(db: AsyncSession, days: int) -> int:
 
15
  ids = (
16
  await db.execute(select(ChatSession.id).where(ChatSession.last_seen < cutoff))
17
  ).scalars().all()
18
+ # Analytics events older than the window are purged too (keeps storage free).
19
+ await db.execute(delete(Event).where(Event.created_at < cutoff))
20
  if not ids:
21
+ await db.flush()
22
  return 0
23
  await db.execute(delete(ChatMessage).where(ChatMessage.session_id.in_(ids)))
24
  await db.execute(delete(ChatSession).where(ChatSession.id.in_(ids)))
tests/routes/test_retention.py CHANGED
@@ -4,7 +4,7 @@ from datetime import UTC, datetime, timedelta
4
 
5
  from sqlalchemy import select
6
 
7
- from app.models import ChatMessage, ChatSession
8
  from app.retention import purge_old_sessions
9
 
10
 
@@ -27,3 +27,15 @@ async def test_purge_removes_old_keeps_recent(db_session):
27
 
28
  msgs = (await db_session.execute(select(ChatMessage))).scalars().all()
29
  assert msgs == []
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
  from sqlalchemy import select
6
 
7
+ from app.models import ChatMessage, ChatSession, Event
8
  from app.retention import purge_old_sessions
9
 
10
 
 
27
 
28
  msgs = (await db_session.execute(select(ChatMessage))).scalars().all()
29
  assert msgs == []
30
+
31
+
32
+ async def test_purge_removes_old_events(db_session):
33
+ now = datetime.now(UTC)
34
+ db_session.add(Event(tenant_id=1, type="turn", created_at=now - timedelta(days=60)))
35
+ db_session.add(Event(tenant_id=1, type="turn", created_at=now))
36
+ await db_session.flush()
37
+
38
+ await purge_old_sessions(db_session, days=30)
39
+
40
+ events = (await db_session.execute(select(Event))).scalars().all()
41
+ assert len(events) == 1 # old event purged, recent kept (keeps Neon free)