|
|
|
|
@@ -367,11 +367,6 @@ function applyTypeSelection(item, selection, text) {
|
|
|
|
|
const hasExplicitUnit = item.unit != null && item.unit !== "";
|
|
|
|
|
const quantityIsGeneric = hasQty && Number(item.quantity) <= 2 && !hasExplicitUnit;
|
|
|
|
|
const needsQuantityClarification = sellsByWeight && quantityIsGeneric;
|
|
|
|
|
|
|
|
|
|
// #region agent log
|
|
|
|
|
fetch("http://127.0.0.1:7242/ingest/86c7b1cd-c414-4eae-852c-08e57e562b3b",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({location:"turnEngineV3.js:applyTypeSelection",message:"type_selection_qty_check",data:{product_name:resolvedProduct?.name,display_unit:resolvedProduct?.display_unit,item_quantity:item.quantity,item_unit:item.unit,hasQty,sellsByWeight,hasExplicitUnit,quantityIsGeneric,needsQuantityClarification,final_status:needsQuantityClarification?"NEEDS_QUANTITY":"READY"},timestamp:Date.now(),sessionId:"debug-session",hypothesisId:"H-qty"})}).catch(()=>{});
|
|
|
|
|
// #endregion
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
...item,
|
|
|
|
|
candidates: [],
|
|
|
|
|
@@ -464,11 +459,6 @@ async function initializePendingItems({
|
|
|
|
|
|
|
|
|
|
audit.catalog_init = audit.catalog_init || [];
|
|
|
|
|
audit.catalog_init.push({ query: mention.query, count: candidates?.length || 0 });
|
|
|
|
|
|
|
|
|
|
// #region agent log
|
|
|
|
|
fetch("http://127.0.0.1:7242/ingest/86c7b1cd-c414-4eae-852c-08e57e562b3b",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({location:"turnEngineV3.js:initializePendingItems",message:"creating_pending_item",data:{query:mention.query,quantity:mention.quantity,unit:mention.unit,candidates_count:candidates?.length||0},timestamp:Date.now(),sessionId:"debug-session",hypothesisId:"H-init"})}).catch(()=>{});
|
|
|
|
|
// #endregion
|
|
|
|
|
|
|
|
|
|
const item = createPendingItem({
|
|
|
|
|
id: `pi_${timestamp}_${i}`,
|
|
|
|
|
query: mention.query,
|
|
|
|
|
@@ -901,11 +891,6 @@ export async function runTurnV3({
|
|
|
|
|
const actions = [];
|
|
|
|
|
const context_patch = {};
|
|
|
|
|
const audit = {};
|
|
|
|
|
|
|
|
|
|
// #region agent log
|
|
|
|
|
fetch("http://127.0.0.1:7242/ingest/86c7b1cd-c414-4eae-852c-08e57e562b3b",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({location:"turnEngineV3.js:898",message:"runTurnV3_entry",data:{prev_state,text_preview:String(text||"").slice(0,50),has_prev_context:!!prev_context,prev_order_basket_exists:!!prev?.order_basket,prev_basket_items_count:prev?.order_basket?.items?.length||0,prev_basket_labels:(prev?.order_basket?.items||[]).map(i=>i.label)},timestamp:Date.now(),sessionId:"debug-session",hypothesisId:"H3-H4"})}).catch(()=>{});
|
|
|
|
|
// #endregion
|
|
|
|
|
|
|
|
|
|
// Observabilidad (NO se envía al LLM)
|
|
|
|
|
audit.trace = {
|
|
|
|
|
tenantId: tenantId || null,
|
|
|
|
|
@@ -929,12 +914,25 @@ export async function runTurnV3({
|
|
|
|
|
};
|
|
|
|
|
const { nlu, raw_text, model, usage, validation } = await llmNluV3({ input: nluInput });
|
|
|
|
|
audit.nlu = { raw_text, model, usage, validation, parsed: nlu };
|
|
|
|
|
|
|
|
|
|
// ─────────────────────────────────────────────────────────────
|
|
|
|
|
// PRIORIDAD: Checkout steps (payment/shipping) tienen prioridad sobre pending_items
|
|
|
|
|
// ─────────────────────────────────────────────────────────────
|
|
|
|
|
const isInCheckoutFlow = prev?.checkout_step === "payment_method" || prev?.checkout_step === "shipping_method";
|
|
|
|
|
const isNumericCheckoutResponse = /^\s*[12]\s*$/.test(String(text || ""));
|
|
|
|
|
const skipPendingItemsForCheckout = isInCheckoutFlow && isNumericCheckoutResponse;
|
|
|
|
|
|
|
|
|
|
// #region agent log
|
|
|
|
|
fetch("http://127.0.0.1:7242/ingest/86c7b1cd-c414-4eae-852c-08e57e562b3b",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({location:"turnEngineV3.js:checkout_priority_check",message:"checkout_flow_check",data:{checkout_step:prev?.checkout_step,isInCheckoutFlow,isNumericCheckoutResponse,skipPendingItemsForCheckout,text,nlu_intent:nlu?.intent},timestamp:Date.now(),sessionId:"debug-session",hypothesisId:"H-checkout"})}).catch(()=>{});
|
|
|
|
|
// #endregion
|
|
|
|
|
|
|
|
|
|
// ─────────────────────────────────────────────────────────────
|
|
|
|
|
// NUEVO FLUJO: Carrito acumulativo con pending_items
|
|
|
|
|
// (Solo si NO estamos en checkout flow con respuesta numérica)
|
|
|
|
|
// ─────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
// 0a) Si hay pending_items existentes, continuar clarificación
|
|
|
|
|
if (Array.isArray(prev?.pending_items) && prev.pending_items.length > 0) {
|
|
|
|
|
if (!skipPendingItemsForCheckout && Array.isArray(prev?.pending_items) && prev.pending_items.length > 0) {
|
|
|
|
|
const hasPendingToProcess = prev.pending_items.some(
|
|
|
|
|
i => i.status === PENDING_ITEM_STATUS.NEEDS_TYPE || i.status === PENDING_ITEM_STATUS.NEEDS_QUANTITY
|
|
|
|
|
);
|
|
|
|
|
@@ -953,13 +951,14 @@ const { nlu, raw_text, model, usage, validation } = await llmNluV3({ input: nluI
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 0b) Si el NLU detecta productos y no hay pending_items, inicializar nuevo flujo
|
|
|
|
|
// (Solo si NO estamos en checkout flow con respuesta numérica)
|
|
|
|
|
const nluIntent = nlu?.intent || "other";
|
|
|
|
|
const isProductIntent = ["add_to_cart", "browse", "price_query"].includes(nluIntent);
|
|
|
|
|
const hasProducts = hasProductMentions(nlu);
|
|
|
|
|
const noPendingItems = !Array.isArray(prev?.pending_items) || prev.pending_items.length === 0;
|
|
|
|
|
const noLegacyPending = !prev?.pending_clarification?.candidates?.length && !prev?.pending_item?.product_id;
|
|
|
|
|
|
|
|
|
|
if (isProductIntent && hasProducts && noPendingItems && noLegacyPending) {
|
|
|
|
|
if (!skipPendingItemsForCheckout && isProductIntent && hasProducts && noPendingItems && noLegacyPending) {
|
|
|
|
|
const result = await initializePendingItems({
|
|
|
|
|
tenantId,
|
|
|
|
|
nlu,
|
|
|
|
|
@@ -977,7 +976,9 @@ const { nlu, raw_text, model, usage, validation } = await llmNluV3({ input: nluI
|
|
|
|
|
|
|
|
|
|
// 0) Procesar multi-items si hay varios productos en un mensaje (LEGACY)
|
|
|
|
|
// Solo si no hay pending_clarification ni pending_item (flujo limpio)
|
|
|
|
|
// Y NO estamos en checkout flow
|
|
|
|
|
if (
|
|
|
|
|
!skipPendingItemsForCheckout &&
|
|
|
|
|
Array.isArray(nlu?.entities?.items) &&
|
|
|
|
|
nlu.entities.items.length > 0 &&
|
|
|
|
|
!prev?.pending_clarification?.candidates?.length &&
|
|
|
|
|
@@ -998,7 +999,8 @@ const { nlu, raw_text, model, usage, validation } = await llmNluV3({ input: nluI
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 1) Resolver pending_clarification primero (LEGACY)
|
|
|
|
|
if (prev?.pending_clarification?.candidates?.length) {
|
|
|
|
|
// Saltar si estamos en checkout flow
|
|
|
|
|
if (!skipPendingItemsForCheckout && prev?.pending_clarification?.candidates?.length) {
|
|
|
|
|
const resolved = resolvePendingSelection({ text, nlu, pending: prev.pending_clarification });
|
|
|
|
|
if (resolved.kind === "more") {
|
|
|
|
|
const nextPending = resolved.pending || prev.pending_clarification;
|
|
|
|
|
@@ -1367,7 +1369,12 @@ if (qty?.quantity) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handler para select_payment: usuario elige método de pago
|
|
|
|
|
if (intent === "select_payment" || (prev?.checkout_step === "payment_method" && (nlu?.entities?.payment_method || nlu?.entities?.selection))) {
|
|
|
|
|
// Detectar número simple como selección cuando estamos en checkout_step payment_method
|
|
|
|
|
const isPaymentSelection = prev?.checkout_step === "payment_method" && /^\s*[12]\s*$/.test(String(text || ""));
|
|
|
|
|
// #region agent log
|
|
|
|
|
fetch("http://127.0.0.1:7242/ingest/86c7b1cd-c414-4eae-852c-08e57e562b3b",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({location:"turnEngineV3.js:select_payment_check",message:"payment_handler_entry",data:{intent,checkout_step:prev?.checkout_step,text,isPaymentSelection,has_payment_method:!!nlu?.entities?.payment_method,has_selection:!!nlu?.entities?.selection,nlu_intent:nlu?.intent},timestamp:Date.now(),sessionId:"debug-session",hypothesisId:"H1-H4"})}).catch(()=>{});
|
|
|
|
|
// #endregion
|
|
|
|
|
if (intent === "select_payment" || isPaymentSelection || (prev?.checkout_step === "payment_method" && (nlu?.entities?.payment_method || nlu?.entities?.selection))) {
|
|
|
|
|
const basketItems = Array.isArray(prev?.order_basket?.items) ? prev.order_basket.items : [];
|
|
|
|
|
|
|
|
|
|
// Determinar método de pago
|
|
|
|
|
@@ -1419,7 +1426,12 @@ if (qty?.quantity) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handler para select_shipping: usuario elige delivery o retiro
|
|
|
|
|
if (intent === "select_shipping" || (prev?.checkout_step === "shipping_method" && (nlu?.entities?.shipping_method || nlu?.entities?.selection))) {
|
|
|
|
|
// Detectar número simple como selección cuando estamos en checkout_step shipping_method
|
|
|
|
|
const isShippingSelection = prev?.checkout_step === "shipping_method" && /^\s*[12]\s*$/.test(String(text || ""));
|
|
|
|
|
// #region agent log
|
|
|
|
|
fetch("http://127.0.0.1:7242/ingest/86c7b1cd-c414-4eae-852c-08e57e562b3b",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({location:"turnEngineV3.js:select_shipping_check",message:"shipping_handler_entry",data:{intent,checkout_step:prev?.checkout_step,text,isShippingSelection,has_shipping_method:!!nlu?.entities?.shipping_method,has_selection:!!nlu?.entities?.selection,nlu_intent:nlu?.intent},timestamp:Date.now(),sessionId:"debug-session",hypothesisId:"H1-H4"})}).catch(()=>{});
|
|
|
|
|
// #endregion
|
|
|
|
|
if (intent === "select_shipping" || isShippingSelection || (prev?.checkout_step === "shipping_method" && (nlu?.entities?.shipping_method || nlu?.entities?.selection))) {
|
|
|
|
|
const basketItems = Array.isArray(prev?.order_basket?.items) ? prev.order_basket.items : [];
|
|
|
|
|
|
|
|
|
|
// Determinar método de envío
|
|
|
|
|
@@ -1661,10 +1673,6 @@ if (qty?.quantity) {
|
|
|
|
|
// Agregar nuevo item
|
|
|
|
|
prevItems.push(item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// #region agent log
|
|
|
|
|
fetch("http://127.0.0.1:7242/ingest/86c7b1cd-c414-4eae-852c-08e57e562b3b",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({location:"turnEngineV3.js:1645",message:"add_to_cart_basket_state",data:{prev_basket_exists:!!prev?.order_basket,prev_basket_items_count:prev?.order_basket?.items?.length||0,prev_items_labels:prevItems.map(i=>i.label),new_item_label:item.label,new_item_qty:item.quantity,new_item_unit:item.unit,was_update:existingIdx>=0},timestamp:Date.now(),sessionId:"debug-session",hypothesisId:"H1-H2"})}).catch(()=>{});
|
|
|
|
|
// #endregion
|
|
|
|
|
context_patch.order_basket = { items: prevItems };
|
|
|
|
|
actions.push({ type: actionType, payload: item });
|
|
|
|
|
const { next_state, validation: v } = safeNextState(prev_state, { ...prev, ...context_patch }, { pending_item_completed: true });
|
|
|
|
|
|