vai-avatar / src /rag-complete.js
bep40's picture
restore to c1013cc7ebfdc93cbb55645441528a84d871a052: 100% restore of target commit tree
9b44afa verified
Raw
History Blame Contribute Delete
5.15 kB
// ── RAG helpers: findProductByName + updatePanelResults ──
// Called by queryProducts() and show_product tool.
function findProductByName(query) {
if (!vaistudioAllProducts.length) return null;
const qs = _sd(query), terms = qs.split(/\s+/).filter(t=>t.length>1);
let best = null, bestScore = 0;
for (const p of vaistudioAllProducts) {
let sc = 0;
const nm = _sd(p.title_clean||p.name), sk = _sd(p.sku||p.model), br = _sd(p.brand);
if (nm===qs||sk===qs) return p;
if (nm.includes(qs)||sk.includes(qs)||br.includes(qs)) sc+=20;
for (const t of terms) { if (nm.includes(t)) sc+=5; if (sk.includes(t)) sc+=4; if (br.includes(t)) sc+=3; }
if (sc>bestScore) { bestScore=sc; best=p; }
}
return best;
}
function updatePanelResults(products) {
const panel = document.getElementById("vaistudio-panel");
const toggle = document.getElementById("vaistudio-toggle");
const loadingEl = document.getElementById("vaistudio-loading");
const prodsEl = document.getElementById("vaistudio-products");
const countEl = document.getElementById("vaistudio-count");
if (!prodsEl) return;
if (panel && !panel.classList.contains("open")) { panel.classList.add("open"); panel.style.display="flex"; }
if (toggle) toggle.classList.add("active");
if (loadingEl) loadingEl.hidden = true;
prodsEl.innerHTML = ""; prodsEl.style.display = "block";
for (let i=0; i<products.length; i++) {
const p = products[i];
const card = document.createElement("div"); card.className = "product-card";
if (p.image) {
const ie = document.createElement("img"); ie.className="product-card-img"; ie.src=p.image; ie.alt=""; ie.loading="lazy";
ie.onerror = function(){this.style.display="none"}; card.appendChild(ie);
} else {
const pl = document.createElement("div"); pl.className="product-card-img";
pl.style.cssText = "flex-shrink:0;background:#e2e8f0;display:flex;align-items:center;justify-content:center;font-size:1.5rem";
pl.textContent="📦"; card.appendChild(pl);
}
const info = document.createElement("div"); info.className="product-card-info";
const t = document.createElement("p"); t.className="product-card-title"; t.textContent=p.title_clean||p.name;
const b = document.createElement("p"); b.className="product-card-brand"; b.textContent=p.brand||"";
const pr = document.createElement("p"); pr.className="product-card-price"; pr.textContent=_fmt(p.priceNum);
info.append(t,b,pr);
if (p.category) { const ct = document.createElement("span"); ct.className="product-card-cat"; ct.textContent=p.category; info.appendChild(ct); }
card.appendChild(info);
card.addEventListener("click", function(e){ e.stopPropagation(); showProductDetail(p); });
prodsEl.appendChild(card);
}
if (countEl) countEl.textContent = products.length+" kết quả";
}
// ── RAG: query_catalog — sync search + UI update ──
/** Search products in memory, update UI panel, return structured text. */
function queryProducts(query) {
if (!vaistudioAllProducts.length) return "Catalog still loading, please wait.";
const q = query.trim();
if (!q) return "What product are you looking for?";
const qNorm = _sd(q), terms = qNorm.split(/\s+/).filter(t=>t.length>1);
const scored = [];
for (let i=0; i<vaistudioAllProducts.length; i++) {
const p = vaistudioAllProducts[i];
let score = 0;
const nm = _sd(p.title_clean||p.name), sk = _sd(p.sku||p.model), br = _sd(p.brand);
const all = [nm, br, _sd(p.category), _sd(p.description||""), _sd((p.features||[]).join(" ")), _sd(p.summary||"")].join(" ");
if (nm.includes(qNorm)) score += 50;
else if (all.includes(qNorm)) score += 30;
for (const t of terms) {
if (nm.includes(t)) score += 8;
else if (sk.includes(t)) score += 6;
else if (br.includes(t)) score += 5;
else if (all.includes(t)) score += 1;
}
if (score > 0) scored.push({ product:p, score });
}
scored.sort((a,b)=>b.score-a.score);
const top = scored.slice(0, 5).map(s=>s.product);
// UPDATE UI PANEL with search results (opens panel, shows cards)
updatePanelResults(top.length ? top : vaistudioAllProducts.slice(0,20));
if (!top.length) return `No products matching "${q}". Try a different term.`;
let result = `Found ${top.length} product(s) matching "${q}":\n\n`;
for (let i=0; i<top.length; i++) {
const p = top[i];
result += `【${i+1}${p.title_clean||p.name}\n Brand: ${p.brand||"N/A"} | Price: ${_fmt(p.priceNum)}\n`;
if (p.model||p.sku) result += ` Model: ${p.model||p.sku}\n`;
if (p.summary) result += ` ${p.summary.slice(0,200)}\n`;
if (p.features?.length) result += ` Features: ${p.features.slice(0,5).join(", ")}${p.features.length>5?"...":""}\n`;
if (p.specs&&Object.keys(p.specs).length) {
const entries = Object.entries(p.specs).filter(([_,v])=>v&&v!=="None").slice(0,4);
for (const [k,v] of entries) result += ` ${k}: ${String(v).slice(0,80)}\n`;
}
result += `\n`;
}
result += `Best: ${top[0].title_clean||top[0].name} (${_fmt(top[0].priceNum)}). Panel updated!`;
return result;
}