Commit Graph

4 Commits

Author SHA1 Message Date
Lucas Tettamanti
675a449ce8 D9 cleanup: borrar NLU/handlers/machine/replyTemplates legacy + activar agente + prompt caching
Después de validar el agente E2E con DeepSeek, el legacy queda muerto.
51 archivos cambiados (la mayoría borrados), el motor único es ahora el
agente tool-calling.

Borrados (~3500 LOC):
- src/modules/3-turn-engine/nlu/ (router + 4 specialists + promptLoader +
  schemas + humanFallback + 6 default prompts) — reemplazado por systemPrompt.js
- src/modules/3-turn-engine/stateHandlers/ (cart.js, cartHelpers.js, idle.js,
  shipping.js, utils.js, index.js) — reemplazado por tools del agente
- src/modules/3-turn-engine/stateHandlers.js (re-export shim)
- src/modules/3-turn-engine/openai.js (NLU clásico v3 + jsonCompletion +
  llmRecommendWriter + llmPlanningRecommend) — el agente crea su propio
  cliente OpenAI con tools nativos
- src/modules/3-turn-engine/replyRewriter.js (rewriting LLM) — el agente
  escribe say directo, no necesita reescribir
- src/modules/3-turn-engine/replyTemplates.js + test (rotación de variantes)
  — el agente varía naturalmente con tool_choice=required + temperature
- src/modules/3-turn-engine/recommendations.js (cross-sell + planning) —
  el agente decide cuándo recomendar via tool calls
- src/modules/3-turn-engine/machine/ (XState v5 completo + 19 tests) —
  reemplazado por la FSM podada en fsm.js + agent/runTurn.js
- src/modules/3-turn-engine/turnEngineV3.helpers.js, .units.js,
  .pendingSelection.js (helpers del legacy)
- src/modules/0-ui/controllers/prompts.js, handlers/prompts.js,
  db/promptsRepo.js — admin de prompts NLU (ya no hay prompts editables)
- public/components/prompts-crud.js + nav entry en ops-shell

turnEngineV3.js se reduce a un thin wrapper que exporta runTurnV3 (alias
de runTurnAgent) + safeNextState (re-export de fsm.js). Mantiene la firma
pública para no tocar pipeline.js.

Activado:
- AGENT_MAX_TOOL_CALLS=10 y AGENT_TURN_TIMEOUT_MS=25000 son los únicos
  flags. Borradas: USE_MODULAR_NLU, USE_XSTATE, XSTATE_SHADOW,
  XSTATE_SETTLE_MS, REPLY_REWRITER, REPLY_REWRITER_TIMEOUT_MS, TURN_ENGINE,
  AGENT_TURN_ENGINE, AGENT_TURN_ENGINE_SHADOW (el agente es default).

Prompt caching DeepSeek:
- systemPrompt.js: era función con storeName interpolado → ahora export
  const SYSTEM_PROMPT (100% estático). storeName se pasa por user message
  via working_memory.store.name. Cualquier cambio al system invalida cache,
  por eso es estático estricto.
- runTurn.js: captura usage.prompt_cache_hit_tokens (DeepSeek) o
  prompt_tokens_details.cached_tokens (OpenAI compat) y suma a métricas.
- /api/metrics/agent ahora reporta prompt_tokens_total,
  completion_tokens_total, prompt_cache_hit_tokens, cache_hit_ratio.
- Smoke test 3 turnos: cache_hit_ratio = 0.72 (17664 cached / 24546 total
  prompt tokens). Saving directo en costo: ~$0.02/M cached vs $0.27/M no
  cached en DeepSeek.

Tests: 148/148 (perdimos 90 tests del legacy XState/replyTemplates que
ya no aplican). Sim flow E2E confirmado: hola → agent responde, multi-turn
con cache caliente.

Si más adelante hace falta volver al legacy: git revert este commit
(c c9c69cf8 es el último estado verde con doble motor).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-02 13:14:59 -03:00
Lucas Tettamanti
6b7889ef4e Mono-tenant: resolver id una vez al boot, eliminar lookups por turno
El sistema nunca fue realmente multi-tenant en la práctica. El esquema
DB conserva las columnas tenant_id (queda lista para escalar más adelante
sin migración), pero la app ahora resuelve el tenant una sola vez al
arranque y todas las capas leen de un único punto.

- src/modules/shared/tenant.js: nuevo módulo. setTenant() en boot,
  getTenantId() lo lee desde cualquier lado.
- index.js: ensureTenant() → setTenant({ id, key }). Sin cambios externos.
- pipeline.resolveTenantId(): pasa de hacer 1-2 queries a DB por turno
  a un return sincrónico del id cacheado. Mantiene firma async para no
  romper callers.
- intake handlers (sim.js, evolution.js): usan getTenantId() directo,
  sin parsing de tenant_key del chat_id ni lookup por canal.
- wooWebhooks: ya no requiere ?tenant_key=... en la query string.
  El webhook va al único tenant configurado.
- repo.js: eliminados getTenantByKey() y getTenantIdByChannel() (no más
  callers).

Plumbing del parámetro tenantId en signatures de handlers/repos/machine
queda intacto — bajar eso es ruido de alto riesgo y no aporta hoy.
188 tests pasando.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 21:00:22 -03:00
Lucas Tettamanti
f784ddd62d Tier 1: chat quality — fuzzy aliases, reply templates, dedup, rewriter
Foco: matar repetición y adaptar respuestas. Los handlers tenían ~30 strings
hardcodeadas (3-7 lugares cada una). Aliases hacían substring exacto.

- pg_trgm + GIN indexes en product_aliases / alias_product_mappings.
  Captura plurales, diminutivos, typos sin reglas. catalogRetrieval re-busca
  el snapshot con normalized_alias cuando el query original no rinde
  (vasio→vacio→Vacío).
- reply_templates table + replyTemplates.js. 20 keys, 2-3 variantes c/u
  con DEFAULTS hardcodeados como fallback. pickVariant excluye las usadas
  en context.recent_replies (FIFO cap 8). Wired en idle/cart/cartHelpers/
  shipping/payment/waiting.
- failed_searches counter en context. count>=3 escala via humanFallback.
  Reset en cada add_to_cart exitoso.
- storeContext.js: vars derivadas de getStoreConfig (delivery_zones, hours,
  zonas) listas para inyectar en templates cuando los datos se carguen.
- replyRewriter.js: LLM call opcional (REPLY_REWRITER=1) que adapta el
  template al hilo conversacional. 1.5s timeout, fallback al template puro.
  Sólo activo en 8 slots semánticamente importantes.
- 12 unit tests para replyTemplates (rotation, recency, FIFO, vars).
  208 tests totales pasando.

Plan completo: ~/.claude/plans/ok-creo-que-tiene-humming-sutton.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 19:29:02 -03:00
Lucas Tettamanti
525679cf8b local dev setup + OPENAI_BASE_URL support + dashboard fix
- CLAUDE.md con arquitectura y comandos del proyecto
- env.example: agregar LIMIT_CONVERSATIONS, MAX_CHARS_PER_MESSAGE, OPENAI_BASE_URL
- docker-compose.override: puerto 3001, extra_hosts para modelo local en Linux
- OpenAI clients: soporte OPENAI_BASE_URL para apuntar a modelo local compatible
- stats.js: sync de órdenes en background, dashboard no bloquea al cargar
- package-lock: dbmate movido a prod dependencies

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 18:32:22 -03:00