Spaces:
Running
Running
AI Quote v5: LLM (Qwen-72B) selects combo based on prompt + fallback random
Browse files- index.html +88 -37
index.html
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
<!DOCTYPE html>
|
| 2 |
<html lang="vi">
|
| 3 |
-
<head><script>window.huggingface={variables:{"SPACE_CREATOR_USER_ID":"661b9191e7b0ab12bceb66f3","VAISTUDIO":""}};</script><script>window.huggingface={variables:{"SPACE_CREATOR_USER_ID":"661b9191e7b0ab12bceb66f3","VAISTUDIO":""}};</script><script>window.huggingface={variables:{"SPACE_CREATOR_USER_ID":"661b9191e7b0ab12bceb66f3","VAISTUDIO":""}};</script><script>window.huggingface={variables:{"SPACE_CREATOR_USER_ID":"661b9191e7b0ab12bceb66f3","VAISTUDIO":""}};</script>
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
<meta name="viewport" content="width=device-width,initial-scale=1">
|
| 6 |
<title>V.AI STUDIO | Niềm tin khách hàng là tài sản của chúng tôi</title>
|
|
@@ -2802,53 +2802,104 @@ function _doSearch(q,res){
|
|
| 2802 |
function doAIQuote(){
|
| 2803 |
const q=document.getElementById('aiSearch').value.trim()||'bếp từ + máy hút mùi + chậu rửa khoảng 50 triệu';
|
| 2804 |
const res=document.getElementById('aiResults');
|
| 2805 |
-
res.style.display='block';
|
| 2806 |
-
|
| 2807 |
-
|
| 2808 |
}
|
| 2809 |
-
function
|
| 2810 |
-
|
| 2811 |
-
|
| 2812 |
-
|
| 2813 |
-
|
| 2814 |
-
|
| 2815 |
-
|
| 2816 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2817 |
let picks=[];let total=0;
|
|
|
|
| 2818 |
wantTypes.forEach(typ=>{
|
| 2819 |
-
// Get ALL matching products with price
|
| 2820 |
let all=D.filter(p=>{
|
| 2821 |
const pn=((p.name||'')+(p.cat||'')).toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/đ/g,'d');
|
| 2822 |
return typ.k.some(k=>pn.includes(k))&&p.priceNum>0&&p.priceNum<=perBudget*1.5;
|
| 2823 |
});
|
| 2824 |
-
//
|
| 2825 |
-
let
|
| 2826 |
-
let
|
| 2827 |
-
|
| 2828 |
-
|
| 2829 |
-
|
| 2830 |
-
shuffle(malloca);shuffle(grob);shuffle(others);
|
| 2831 |
-
// Build pool: random Malloca (60%) + Grob (30%) + others (10%)
|
| 2832 |
-
let pool=[];
|
| 2833 |
-
pool.push(...malloca.slice(0,Math.max(3,Math.ceil(malloca.length*0.6))));
|
| 2834 |
-
pool.push(...grob.slice(0,Math.max(2,Math.ceil(grob.length*0.3))));
|
| 2835 |
-
pool.push(...others.slice(0,2));
|
| 2836 |
-
shuffle(pool);// Final shuffle
|
| 2837 |
-
// Filter by budget
|
| 2838 |
-
let affordable=pool.filter(p=>p.priceNum<=perBudget*1.3);
|
| 2839 |
-
if(!affordable.length)affordable=pool.slice(0,5);
|
| 2840 |
-
// Pick ONE random
|
| 2841 |
-
if(affordable.length){
|
| 2842 |
-
let pick=affordable[Math.floor(Math.random()*affordable.length)];
|
| 2843 |
-
picks.push({...pick,typeName:typ.n});total+=pick.priceNum;
|
| 2844 |
-
}
|
| 2845 |
});
|
| 2846 |
-
if(!picks.length){res.innerHTML='😅 Không đủ SP
|
| 2847 |
-
|
|
|
|
|
|
|
|
|
|
| 2848 |
h+='<table style="width:100%;border-collapse:collapse;font-size:.8rem">';
|
| 2849 |
h+='<tr style="background:#003f62;color:#fff"><th style="padding:8px;text-align:left">Loại</th><th style="text-align:left">Sản phẩm</th><th>Brand</th><th style="text-align:right;padding-right:8px">Giá</th></tr>';
|
| 2850 |
picks.forEach((p,i)=>{
|
| 2851 |
-
h+='<tr style="border-bottom:1px solid #e2e8f0;'+(i%2?'background:#f8fafc':'')+'"><td style="padding:8px;font-weight:600">'+p.typeName+'</td><td>'+p.name.substring(0,38)+'<br><span style="font-size:.65rem;color:#888">'+p.sku+'</span></td><td style="text-align:center">'+p.brand+'</td><td style="text-align:right;font-weight:700;color:#003f62;padding-right:8px">'+(p.price||'LH')+'</td></tr>';
|
| 2852 |
});
|
| 2853 |
h+='<tr style="background:#003f62;color:#fff;font-weight:800"><td colspan="3" style="padding:10px;text-align:right">TỔNG:</td><td style="text-align:right;padding-right:8px;font-size:.95rem">'+total.toLocaleString('vi')+'đ</td></tr></table>';
|
| 2854 |
const diff=budget-total;
|
|
|
|
| 1 |
<!DOCTYPE html>
|
| 2 |
<html lang="vi">
|
| 3 |
+
<head><script>window.huggingface={variables:{"SPACE_CREATOR_USER_ID":"661b9191e7b0ab12bceb66f3","VAISTUDIO":""}};</script><script>window.huggingface={variables:{"SPACE_CREATOR_USER_ID":"661b9191e7b0ab12bceb66f3","VAISTUDIO":""}};</script><script>window.huggingface={variables:{"SPACE_CREATOR_USER_ID":"661b9191e7b0ab12bceb66f3","VAISTUDIO":""}};</script><script>window.huggingface={variables:{"SPACE_CREATOR_USER_ID":"661b9191e7b0ab12bceb66f3","VAISTUDIO":""}};</script><script>window.huggingface={variables:{"SPACE_CREATOR_USER_ID":"661b9191e7b0ab12bceb66f3","VAISTUDIO":""}};</script>
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
<meta name="viewport" content="width=device-width,initial-scale=1">
|
| 6 |
<title>V.AI STUDIO | Niềm tin khách hàng là tài sản của chúng tôi</title>
|
|
|
|
| 2802 |
function doAIQuote(){
|
| 2803 |
const q=document.getElementById('aiSearch').value.trim()||'bếp từ + máy hút mùi + chậu rửa khoảng 50 triệu';
|
| 2804 |
const res=document.getElementById('aiResults');
|
| 2805 |
+
res.style.display='block';
|
| 2806 |
+
res.innerHTML='<div style="text-align:center;padding:20px"><i class="fas fa-robot fa-spin" style="font-size:1.5rem;color:#003f62"></i><br><span style="font-size:.85rem;margin-top:8px;display:block">AI đang phân tích yêu cầu & chọn combo tốt nhất...</span></div>';
|
| 2807 |
+
_aiSelectProducts(q,res);
|
| 2808 |
}
|
| 2809 |
+
async function _aiSelectProducts(q,res){
|
| 2810 |
+
try{
|
| 2811 |
+
if(typeof D==='undefined'||!D.length){res.innerHTML='⏳ Đang tải SP...';return}
|
| 2812 |
+
// Parse budget
|
| 2813 |
+
const bm=q.match(/(\d+)\s*(tr|triệu|m)/i);
|
| 2814 |
+
const budget=bm?parseInt(bm[1])*1000000:50000000;
|
| 2815 |
+
// Get product summary for AI (top products per category, Malloca/Grob priority)
|
| 2816 |
+
const typeMap=[{k:['bep tu','bep dien','bep'],n:'Bếp từ'},{k:['may hut','hut mui'],n:'Máy hút mùi'},{k:['chau rua','bon rua'],n:'Chậu rửa'},{k:['voi rua'],n:'Vòi rửa'},{k:['lo nuong'],n:'Lò nướng'}];
|
| 2817 |
+
const qn=q.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/đ/g,'d');
|
| 2818 |
+
let wantTypes=typeMap.filter(t=>t.k.some(k=>qn.includes(k)));
|
| 2819 |
+
if(!wantTypes.length)wantTypes=typeMap.slice(0,3);
|
| 2820 |
+
const perBudget=Math.floor(budget/wantTypes.length);
|
| 2821 |
+
// Build catalog summary for AI
|
| 2822 |
+
let catalog=[];
|
| 2823 |
+
wantTypes.forEach(typ=>{
|
| 2824 |
+
let items=D.filter(p=>{
|
| 2825 |
+
const pn=((p.name||'')+(p.cat||'')).toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/đ/g,'d');
|
| 2826 |
+
return typ.k.some(k=>pn.includes(k))&&p.priceNum>0&&p.priceNum<=perBudget*1.5;
|
| 2827 |
+
});
|
| 2828 |
+
// Shuffle and take samples for AI to choose from
|
| 2829 |
+
for(let i=items.length-1;i>0;i--){let j=Math.floor(Math.random()*(i+1));[items[i],items[j]]=[items[j],items[i]]}
|
| 2830 |
+
// Priority: Malloca/Grob first in the list AI sees
|
| 2831 |
+
let m=items.filter(p=>/malloca/i.test(p.brand));
|
| 2832 |
+
let g=items.filter(p=>/grob/i.test(p.brand));
|
| 2833 |
+
let o=items.filter(p=>!/malloca|grob/i.test(p.brand));
|
| 2834 |
+
let pool=[...m.slice(0,5),...g.slice(0,4),...o.slice(0,3)];
|
| 2835 |
+
pool.forEach(p=>catalog.push({sku:p.sku,name:p.name.substring(0,50),brand:p.brand,price:p.priceNum,type:typ.n}));
|
| 2836 |
+
});
|
| 2837 |
+
// Call AI to select best combo
|
| 2838 |
+
const aiPrompt=`Khách yêu cầu: "${q}"\nNgân sách: ${budget.toLocaleString('vi')}đ\nDanh sách SP có sẵn:\n${catalog.map((p,i)=>i+1+'. ['+p.sku+'] '+p.name+' | '+p.brand+' | '+p.price.toLocaleString('vi')+'đ | Loại: '+p.type).join('\n')}\n\nChọn combo TỐT NHẤT (1 SP mỗi loại, ưu tiên Malloca/Grob, tổng ≤ ngân sách). Trả lời CHỈ JSON: {"picks":[{"sku":"...","reason":"lý do chọn ngắn"}]}`;
|
| 2839 |
+
const aiRes=await fetch('https://router.huggingface.co/v1/chat/completions',{
|
| 2840 |
+
method:'POST',
|
| 2841 |
+
headers:{'Content-Type':'application/json','Authorization':'Bearer '+document.querySelector('meta[name=hf-token]')?.content||''},
|
| 2842 |
+
body:JSON.stringify({model:'Qwen/Qwen2.5-72B-Instruct',messages:[{role:'user',content:aiPrompt}],max_tokens:300,temperature:0.7})
|
| 2843 |
+
});
|
| 2844 |
+
if(!aiRes.ok){
|
| 2845 |
+
// Fallback to random selection if AI fails
|
| 2846 |
+
_doQuoteFallback(wantTypes,perBudget,budget,res);return;
|
| 2847 |
+
}
|
| 2848 |
+
const aiData=await aiRes.json();
|
| 2849 |
+
const content=aiData.choices[0].message.content;
|
| 2850 |
+
const jsonMatch=content.match(/\{[\s\S]*\}/);
|
| 2851 |
+
if(!jsonMatch){_doQuoteFallback(wantTypes,perBudget,budget,res);return}
|
| 2852 |
+
const result=JSON.parse(jsonMatch[0]);
|
| 2853 |
+
const selectedSkus=result.picks.map(p=>p.sku);
|
| 2854 |
+
const reasons=Object.fromEntries(result.picks.map(p=>[p.sku,p.reason||'']));
|
| 2855 |
+
// Map SKUs to actual products
|
| 2856 |
+
let picks=[];let total=0;
|
| 2857 |
+
selectedSkus.forEach(sku=>{
|
| 2858 |
+
const found=D.find(p=>p.sku===sku);
|
| 2859 |
+
if(found){
|
| 2860 |
+
const typ=catalog.find(c=>c.sku===sku);
|
| 2861 |
+
picks.push({...found,typeName:typ?typ.type:'SP',reason:reasons[sku]||''});
|
| 2862 |
+
total+=found.priceNum;
|
| 2863 |
+
}
|
| 2864 |
+
});
|
| 2865 |
+
if(!picks.length){_doQuoteFallback(wantTypes,perBudget,budget,res);return}
|
| 2866 |
+
_renderQuote(picks,total,budget,res,true);
|
| 2867 |
+
}catch(e){
|
| 2868 |
+
console.error(e);
|
| 2869 |
+
// Fallback
|
| 2870 |
+
const bm=q.match(/(\d+)\s*(tr|triệu|m)/i);
|
| 2871 |
+
const budget=bm?parseInt(bm[1])*1000000:50000000;
|
| 2872 |
+
const typeMap=[{k:['bep tu','bep dien','bep'],n:'Bếp từ'},{k:['may hut','hut mui'],n:'Máy hút mùi'},{k:['chau rua','bon rua'],n:'Chậu rửa'}];
|
| 2873 |
+
const qn=q.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/đ/g,'d');
|
| 2874 |
+
let wantTypes=typeMap.filter(t=>t.k.some(k=>qn.includes(k)));
|
| 2875 |
+
if(!wantTypes.length)wantTypes=typeMap.slice(0,3);
|
| 2876 |
+
_doQuoteFallback(wantTypes,Math.floor(budget/wantTypes.length),budget,res);
|
| 2877 |
+
}
|
| 2878 |
+
}
|
| 2879 |
+
function _doQuoteFallback(wantTypes,perBudget,budget,res){
|
| 2880 |
let picks=[];let total=0;
|
| 2881 |
+
function shuffle(a){for(let i=a.length-1;i>0;i--){let j=Math.floor(Math.random()*(i+1));[a[i],a[j]]=[a[j],a[i]]}return a}
|
| 2882 |
wantTypes.forEach(typ=>{
|
|
|
|
| 2883 |
let all=D.filter(p=>{
|
| 2884 |
const pn=((p.name||'')+(p.cat||'')).toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/đ/g,'d');
|
| 2885 |
return typ.k.some(k=>pn.includes(k))&&p.priceNum>0&&p.priceNum<=perBudget*1.5;
|
| 2886 |
});
|
| 2887 |
+
let m=shuffle(all.filter(p=>/malloca/i.test(p.brand)));
|
| 2888 |
+
let g=shuffle(all.filter(p=>/grob/i.test(p.brand)));
|
| 2889 |
+
let pool=[...m.slice(0,4),...g.slice(0,3),...shuffle(all).slice(0,3)];
|
| 2890 |
+
shuffle(pool);
|
| 2891 |
+
let pick=pool.find(p=>p.priceNum<=perBudget*1.3)||pool[0];
|
| 2892 |
+
if(pick){picks.push({...pick,typeName:typ.n,reason:''});total+=pick.priceNum}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2893 |
});
|
| 2894 |
+
if(!picks.length){res.innerHTML='😅 Không đủ SP phù hợp';return}
|
| 2895 |
+
_renderQuote(picks,total,budget,res,false);
|
| 2896 |
+
}
|
| 2897 |
+
function _renderQuote(picks,total,budget,res,isAI){
|
| 2898 |
+
let h='<div style="font-weight:700;margin-bottom:10px;font-size:.95rem">'+(isAI?'🤖':'📋')+' AI BÁO GIÁ <span style="color:#64748b;font-weight:400">(~'+budget.toLocaleString('vi')+'đ)</span></div>';
|
| 2899 |
h+='<table style="width:100%;border-collapse:collapse;font-size:.8rem">';
|
| 2900 |
h+='<tr style="background:#003f62;color:#fff"><th style="padding:8px;text-align:left">Loại</th><th style="text-align:left">Sản phẩm</th><th>Brand</th><th style="text-align:right;padding-right:8px">Giá</th></tr>';
|
| 2901 |
picks.forEach((p,i)=>{
|
| 2902 |
+
h+='<tr style="border-bottom:1px solid #e2e8f0;'+(i%2?'background:#f8fafc':'')+'"><td style="padding:8px;font-weight:600">'+p.typeName+'</td><td>'+p.name.substring(0,38)+(p.reason?'<br><span style="font-size:.65rem;color:#28a745">💡 '+p.reason+'</span>':'')+'<br><span style="font-size:.65rem;color:#888">'+p.sku+'</span></td><td style="text-align:center;font-size:.75rem">'+p.brand+'</td><td style="text-align:right;font-weight:700;color:#003f62;padding-right:8px">'+(p.price||'LH')+'</td></tr>';
|
| 2903 |
});
|
| 2904 |
h+='<tr style="background:#003f62;color:#fff;font-weight:800"><td colspan="3" style="padding:10px;text-align:right">TỔNG:</td><td style="text-align:right;padding-right:8px;font-size:.95rem">'+total.toLocaleString('vi')+'đ</td></tr></table>';
|
| 2905 |
const diff=budget-total;
|