deterministic-interpreter.js — intérprete determinista NLU-02A
Overview
Este módulo convierte una entrada de InterpretationResult.input en un InterpretationResult válido en ejecución. Lo hace sin E/S, sin reloj, sin aleatoriedad, sin estado de módulo y sin proveedor de IA. También mantiene la evidencia textual que no puede representar, en lugar de descartarla.
Su papel es muy concreto dentro de services/api/src/modules/interpretation: usa el contrato de interpretación, el léxico inmobiliario, la normalización de texto, los montos deterministas y la ubicación determinista. Esa combinación le permite producir resultados reproducibles y validados. fileciteturn0file0turn0file9
[!IMPORTANT] Este intérprete nunca ejecuta acciones de dominio. Como mucho, propone
publish_wizard.openpara una oferta.
[!NOTE] La validación final pasa por
validateRuntimeInterpretation. El resultado también se congela en profundidad antes de devolverse. fileciteturn0file9turn0file13
How it works
flowchart TB
A[interpret] --> B[snapshotInput]
B --> C[interpretText]
C --> D[analyze]
D --> E[decideIntent]
E -->|discover.search| F[buildSearch]
E -->|publish.start_offer| G[buildOffer]
E -->|ambiguous| H[buildAmbiguous]
E -->|unsupported| I[buildUnsupported]
E -->|unrecognized| J[buildUnrecognized]
F --> K[validateRuntimeInterpretation]
G --> K
H --> K
I --> K
J --> K
K --> L[deepFreeze]
El flujo empieza con snapshotInput. Esa función valida que la entrada sea un objeto plano y serializable. Después, interpretText analiza solo text. Si el texto es inválido o demasiado largo, el intérprete usa una cadena vacía.
analyze combina varias señales. Extrae coincidencias del léxico, ubicaciones, negaciones y montos. Después, decideIntent selecciona entre búsqueda, oferta, ambigüedad, no soportado o no reconocido. Finalmente, el builder correspondiente arma el resultado y validateRuntimeInterpretation verifica que todo cumpla el contrato. fileciteturn0file9turn0file17
Key components
{
"title": "InterpretationError",
"content": "Clase de error del m\u00f3dulo. Representa fallos de entrada o de resultado con el c\u00f3digo INVALID_INPUT o INVALID_RESULT.",
"type": "class",
"filePath": "services/api/src/modules/interpretation/deterministic-interpreter.js",
"badges": [
"public"
]
}
{
"title": "interpret",
"content": "Funci\u00f3n p\u00fablica principal. Recibe la entrada NLU-01, ejecuta el int\u00e9rprete determinista, valida el resultado y lo devuelve congelado.",
"type": "function",
"filePath": "services/api/src/modules/interpretation/deterministic-interpreter.js",
"badges": [
"public"
]
}
Data & fields
[
{
"name": "contractVersion",
"type": "string",
"required": true,
"description": "Versión del contrato devuelta por INTERPRETATION_CONTRACT_VERSION."
},
{
"name": "interpreter",
"type": "object",
"required": true,
"description": "Metadata del intérprete determinista.",
"children": [
{
"name": "kind",
"type": "string",
"required": true,
"description": "Siempre es deterministic."
},
{
"name": "id",
"type": "string",
"required": true,
"description": "Identificador exportado por DETERMINISTIC_INTERPRETER_ID."
},
{
"name": "version",
"type": "string",
"required": true,
"description": "Versión semántica del intérprete."
},
{
"name": "capabilityRegistryVersion",
"type": "string",
"required": true,
"description": "Versión del registro de capacidades vigente."
},
{
"name": "lexiconVersion",
"type": "string",
"required": true,
"description": "Versión del léxico inmobiliario vigente."
}
]
},
{
"name": "input",
"type": "object",
"required": true,
"description": "Copia profunda de la entrada recibida. El intérprete solo lee text y conserva el resto."
},
{
"name": "status",
"type": "string",
"required": true,
"description": "Estado runtime. Puede ser recognized, needs_clarification, ambiguous_intent, unsupported o unrecognized."
},
{
"name": "intent",
"type": "string | null",
"required": true,
"description": "Intento resuelto. Usa discover.search, publish.start_offer o null."
},
{
"name": "params",
"type": "object",
"required": true,
"description": "Parámetros extraídos con procedencia textual exacta.",
"children": [
{
"name": "propertyType",
"type": "object",
"description": "Tipo de inmueble extraído del léxico.",
"children": [
{
"name": "value",
"type": "string",
"required": true,
"description": "Valor canónico del tipo de inmueble."
},
{
"name": "source",
"type": "object",
"required": true,
"description": "Procedencia exacta del fragmento.",
"children": [
{
"name": "kind",
"type": "string",
"required": true,
"description": "Siempre text."
},
{
"name": "span",
"type": "number[]",
"required": true,
"description": "Rango UTF-16 del fragmento original."
},
{
"name": "matched",
"type": "string",
"required": true,
"description": "Fragmento exacto encontrado."
},
{
"name": "rule",
"type": "string",
"required": true,
"description": "Regla o identificador de origen."
}
]
}
]
},
{
"name": "entityType",
"type": "object",
"description": "Se fija en property cuando existe propertyType."
},
{
"name": "operationType",
"type": "object",
"description": "Operación inmobiliaria reconocida, como rent o sell."
},
{
"name": "locationQuery",
"type": "object",
"description": "Ubicación textual extraída sin geocodificación."
},
{
"name": "budgetMax",
"type": "object",
"description": "Cota superior de presupuesto.",
"children": [
{
"name": "value",
"type": "number",
"required": true,
"description": "Monto máximo normalizado."
},
{
"name": "source",
"type": "object",
"required": true,
"description": "Fragmento de origen del monto."
}
]
},
{
"name": "budgetCurrency",
"type": "object",
"description": "Moneda explícita o resuelta por contexto."
},
{
"name": "budgetPeriodicity",
"type": "object",
"description": "Periodicidad monetaria reconocida."
}
]
},
{
"name": "missing",
"type": "array",
"required": true,
"description": "Campos que faltan para completar la interpretación.",
"children": [
{
"name": "param",
"type": "string",
"required": true,
"description": "Nombre del parámetro faltante."
},
{
"name": "required",
"type": "boolean",
"required": true,
"description": "Indica si el faltante bloquea la interpretación."
},
{
"name": "control",
"type": "string",
"required": true,
"description": "Tipo de control de aclaración."
}
]
},
{
"name": "ambiguities",
"type": "array",
"required": true,
"description": "Ambigüedades que requieren elección explícita.",
"children": [
{
"name": "source",
"type": "object",
"required": true,
"description": "Fragmento que originó la ambigüedad."
},
{
"name": "control",
"type": "string",
"required": true,
"description": "Tipo de control que resuelve la ambigüedad."
},
{
"name": "options",
"type": "array",
"required": true,
"description": "Opciones disponibles para resolverla."
}
]
},
{
"name": "unparsed",
"type": "array",
"required": true,
"description": "Evidencia reconocida pero no representable.",
"children": [
{
"name": "span",
"type": "number[]",
"required": true,
"description": "Rango UTF-16 exacto."
},
{
"name": "matched",
"type": "string",
"required": true,
"description": "Fragmento exacto conservado."
},
{
"name": "reason",
"type": "string",
"required": true,
"description": "Motivo de la conservación."
}
]
},
{
"name": "capability",
"type": "object",
"description": "Capacidad declarada para la acción propuesta o planificada."
},
{
"name": "proposedAction",
"type": "object | null",
"description": "Acción sugerida por el intérprete. En esta versión solo aparece para publish.start_offer."
},
{
"name": "confirmation",
"type": "string",
"required": true,
"description": "Nivel de confirmación asociado a la acción o al resultado."
}
]
Constants and rule sets
DETERMINISTIC_INTERPRETER_IDfija el identificador público del intérprete comoplano-deterministic.DETERMINISTIC_INTERPRETER_VERSIONfija la versión del intérprete como0.1.0.INTERPRETATION_ERROR_CODESexponeINVALID_INPUTeINVALID_RESULT.AMBIGUOUS_INTENT_OPTIONScontienediscover.searchypublish.start_offer.DOLLAR_OPTIONSmodela la ambigüedad de$comoMXNoUSD.NARROW_NEGATIONSyNARROW_SKIP_WORDSdefinen el alcance de negaciones estrechas.OBJECT_BOUNDARY_WORDSyDETERMINER_WORDSayudan a cortar objetos negados.CONFIDENT_SCOPE_WORDSmarca el umbral de incertidumbre para cláusulas largas.CONJUNCTION_BREAKSyVERB_BREAKScierran el alcance de una cláusula.POSSESSIVE_BEFOREdetecta posesivos como evidencia de oferta.SCOPE_WORDyCLAUSE_BREAKsostienen la segmentación textual.
Behaviors
isPlainObject— Comprueba si un valor es un objeto plano.facetsOf— Devuelve facetas solo cuando la coincidencia no es ambigua.facetValue— Lee el valor de una faceta por categoría.textSource— Construye procedencia textual conspan,matchedyrule.unparsedItem— Crea un registro de evidencia no representable.deepFreeze— Congela recursivamente objetos y sus hijos.negatedClause— Detecta el final de una cláusula negada.unrepresentableObjectEnd— Calcula el alcance de una negación sin criterios representables.resolveNegations— Marca coincidencias y lugares negados, y conserva evidencia.single— Resuelve un único valor de faceta o detecta conflicto.analyze— Extrae señales de léxico, lugares, negaciones y montos.decideIntent— Decide el intent final con evidencia textual.propertyParams— ConstruyepropertyType,entityType,operationTypeylocationQuery.distinctPlaces— Deduplica lugares por valor.sharedUnparsed— Reúne evidencia no representable compartida.orderedUnparsed— Elimina duplicados y ordena evidencia de forma determinista.capabilityFor— Proyecta una acción del registro a su capacidad.buildSearch— Construye una búsqueda reconocida o con aclaraciones.buildOffer— Construye una oferta reconocida y propone abrir el wizard.buildAmbiguous— Devuelve un resultado conambiguous_intent.buildUnsupported— Devuelve un resultadounsupported.buildUnrecognized— Devuelve un resultadounrecognized.interpretText— Despacha al builder correcto según el intent.snapshotInput— Copia y valida la entrada como datos serializables.interpret— Orquesta snapshot, interpretación, validación y congelado final. fileciteturn0file9turn0file13
Gotchas & edge cases
[!WARNING] Si
inputno es un objeto plano,interpretlanzaINVALID_INPUT. UnProxyrevocado también cuenta como entrada inválida.
[!WARNING] Si
inputno es serializable,snapshotInputfalla conINVALID_INPUT. El módulo no continúa con una copia parcial.
[!CAUTION] Un texto ausente o demasiado largo se reemplaza por
"". El intérprete no procesa ese contenido.
[!IMPORTANT] La negación nunca activa un criterio positivo. Si algo no puede representarse, queda en
unparsedcon su fragmento exacto. fileciteturn0file0turn0file9
[!NOTE]
"$"por sí solo no fijaMXN. El módulo deja esa ambigüedad enambiguities.
[!NOTE]
1.500sin magnitud no se interpreta como monto válido. La evidencia se conserva.
[!NOTE] La ubicación se extrae por estructura, no por mayúsculas. El módulo puede aceptar texto como
puebla. fileciteturn0file0turn0file1
Related
deterministic-interpreter.js depende de interpretation-contract.js, real-estate-lexicon.js, text-normalization.js, deterministic-amounts.js y deterministic-location.js. Esa relación se ve directamente en sus imports. fileciteturn0file0turn0file13
Los tests de services/api/test/interpretation-deterministic.test.js, services/api/test/interpretation-deterministic-units.test.js y services/api/test/interpretation-deterministic-dev.test.js validan versiones, procedencia exacta, determinismo y acuerdo con el corpus. El contrato también se cubre en services/api/test/interpretation-contract.test.js. fileciteturn0file2turn0file4turn0file5turn0file8turn0file14
El contexto del repositorio describe este módulo como NLU-02A, un primer corte vertical de interpretación determinista. También señala que su salida sigue el contrato InterpretationResult de NLU-01. fileciteturn0file1turn0file6