pini-print-bot / engine /classifier.js
dotandru's picture
feat(v5.7.4): Semantic Closure Gate, Delete unused files, PDF Fixes
d897af7
Raw
History Blame Contribute Delete
3.43 kB
/** engine/classifier.js V101.0 - Hybrid Vision Integration */
const { routeWithLLM } = require('../services/llmService');
const { downloadFile } = require('../services/storageService');
const { processImageUpload } = require('./imageProcessor');
const KEYWORDS = {
reset: ['reset', 'ื”ืชื—ืœ', 'ืื™ืคื•ืก', 'ืจื™ืกื˜', 'ืชืคืจื™ื˜'],
cart: ['ืขื’ืœื”', 'ืกื™ื›ื•ื', 'ื›ืžื” ื–ื” ื™ื•ืฆื', 'ื›ืžื” ื™ืฆื'],
checkout: ['ืชืืจื•ื–', 'ื”ืฆืขืช ืžื—ื™ืจ', 'ืœืฉืœื', 'ื—ืฉื‘ื•ืŸ', 'ืฆ\'ืง ืืื•ื˜', 'checkout']
};
async function classify(text, session) {
const safeText = String(text || "");
const t = safeText.toLowerCase().trim();
// 1. Technical Fast Paths
if (t.startsWith('system_')) return { intent: 'system_action', action: t, raw_text: safeText };
if (KEYWORDS.reset.some(k => t.includes(k)) && t.split(' ').length <= 4) return { intent: 'reset' };
if (KEYWORDS.cart.some(k => t.includes(k))) return { intent: 'show_cart' };
// 2. LLM / Compiler Pipeline
try {
let imageBuffer = null;
let technicalMetadata = null;
if (safeText.includes('[IMAGE_UPLOADED:')) {
const match = safeText.match(/\[IMAGE_UPLOADED:\s*([^\]]+)\]/);
if (match) {
try {
imageBuffer = await downloadFile(match[1].trim());
// LAYER 1: The Code is Judge (Deterministic physical measurement)
technicalMetadata = await processImageUpload(imageBuffer);
session.lastImageMetadata = technicalMetadata; // Persist for context
} catch (e) {
console.error("โŒ [VISION ERROR]:", e.message);
}
}
}
const extraction = await routeWithLLM(safeText, session, imageBuffer);
extraction.raw_text = safeText;
extraction.technicalMetadata = technicalMetadata;
// --- Spec v5.7.4: Deterministic Semantic Closure Gate ---
// Calculate closure intent and confirmation flag heuristically
let closureScore = 0;
let isConfirmed = false;
const positiveClosureSignals = ["ื”ืฉืืจ", "ื”ื›ืœ ืžื•ืฉืœื", "ืกื’ื•ืจ", "ืชืŸ ื”ืฆืขื”", "ืžืขื•ืœื”", "ื‘ื“ื™ื•ืง", "ื–ื”ื•", "ืชืชืงื“ื", "ืฉืœื— ื‘ื”ืงื“ื", "ื”ืžืฉืš", "ื‘ืกื“ืจ", "ืžื•ื›ืŸ"];
const conditionSignals = ["ืื‘ืœ", "ื—ื•ืฅ ืž", "ืื•ืœื™", "ืœืฉื ื•ืช", "ืจื’ืข", "ืฉื ื”", "ืชื—ืœื™ืฃ", "ื‘ืžืงื•ื"];
const hasPositiveSignal = positiveClosureSignals.some(s => t.includes(s));
const hasCondition = conditionSignals.some(s => t.includes(s));
if (hasPositiveSignal && !hasCondition && extraction.intent !== 'update' && extraction.intent !== 'reset') {
closureScore = 0.9;
isConfirmed = true;
} else if (hasCondition || extraction.intent === 'update') {
closureScore = 0.0;
isConfirmed = false;
}
extraction.closure_intent_score = closureScore;
extraction.confirmation_flag = isConfirmed;
return extraction;
} catch (e) {
console.error("Classifier Error:", e);
return {
intent: "chat",
answer_text: "ืกืœื™ื—ื”, ืื ื™ ื—ื•ื•ื” ืงื•ืฉื™ ืงื˜ืŸ ื‘ืขื™ื‘ื•ื“. ื ืกื” ืฉื•ื‘?",
products_detected: [],
parameters_detected: []
};
}
}
module.exports = { classifyMessage: classify };