!function(){"use strict";const e=document.querySelector('.tool-workspace[data-tool="llm-config"]');if(!e)return;const t=t=>e.querySelector(t),a={"rtx3060-6":{vram:6,vendor:"nvidia",name:"RTX 3060 6GB"},"rtx4060-8":{vram:8,vendor:"nvidia",name:"RTX 4060 8GB"},"rtx4070-12":{vram:12,vendor:"nvidia",name:"RTX 4070 12GB"},"rtx3090-24":{vram:24,vendor:"nvidia",name:"RTX 3090 24GB"},"rtx4090-24":{vram:24,vendor:"nvidia",name:"RTX 4090 24GB"},"rtx5090-32":{vram:32,vendor:"nvidia",name:"RTX 5090 32GB"},"a100-40":{vram:40,vendor:"nvidia",name:"A100 40GB"},"a100-80":{vram:80,vendor:"nvidia",name:"A100 80GB"},"h100-80":{vram:80,vendor:"nvidia",name:"H100 80GB"},"apple-24":{vram:24,vendor:"apple",name:"Apple Silicon 24GB"},"amd-16":{vram:16,vendor:"amd",name:"AMD GPU 16GB"},custom:{vram:6,vendor:"custom",name:"自定义 GPU"}},n={bf16:2.05,fp8:1.08,q8:1.08,q6:.82,q5:.7,q4:.58,q3:.46},r={bf16:"BF16/FP16",fp8:"FP8",q8:"INT8/Q8",q6:"Q6",q5:"Q5",q4:"Q4",q3:"Q3"};let o="";const i=e=>t('[data-config="'+e+'"]').value,l=e=>Math.max(0,Number(i(e))||0),m=e=>new Intl.NumberFormat("zh-CN",{maximumFractionDigits:2}).format(e),c=(e,t,a)=>{const n=document.createElement(e);return t&&(n.className=t),void 0!==a&&(n.textContent=a),n};function u(){const e=function(){const e=a[i("gpu")];return{requestedRuntime:i("runtime"),gpu:e,gpuCount:Math.max(1,l("gpuCount")),vram:l("vram"),ram:l("ram"),cores:Math.max(1,l("cores")),disk:l("disk"),architecture:i("architecture"),params:Math.max(.1,l("params")),activeParams:Math.max(.1,l("activeParams")),quant:i("quant"),modelFormat:i("format"),context:l("context"),concurrency:Math.max(1,l("concurrency")),model:i("model").trim()||"/models/model"}}(),u=function(e){const t=e.vram*e.gpuCount,a=.9*t,r=e.params*n[e.quant]*.9313,o="bf16"===e.quant?1:.56,i=122e-6*Math.sqrt(e.params/7)*o*e.context*e.concurrency,l=Math.max(.8,.07*r)+.28*e.concurrency,m=r+i+l,c=Math.max(0,m-a),u=1.2*r+2,d=Math.max(4,1.2*c+4);let s=e.requestedRuntime;"auto"===s&&(s="gguf"===e.modelFormat||"apple"===e.gpu.vendor||"amd"===e.gpu.vendor?"llama":"ftw"===e.modelFormat||"moe"===e.architecture&&"nvidia"===e.gpu.vendor&&e.params>=20&&e.ram>=1.1*r+6?"freetoken":"vllm");const v=m<=a,p="llama"===s||"freetoken"===s||"vllm"===s,g=e.ram>=d,h=e.disk>=u;return{totalVram:t,usableVram:a,weights:r,kv:i,runtimeOverhead:l,gpuNeed:m,offload:c,diskNeed:u,ramNeed:d,runtime:s,directFit:v,ramFit:g,diskFit:h,level:v?"ok":p&&g&&h?"warn":"danger"}}(e),d=function(e,t){const a=Math.max(1,e.cores-(e.cores>=8?2:1)),n=e.vram<=8?512:e.vram<=16?1024:2048,r=e.vram<=8?128:256,o=t.gpuNeed>.85*t.usableVram;let i;if("llama"===t.runtime)return i=["llama-server \\",' --model "'+e.model+'" \\'," --ctx-size "+e.context+" --parallel "+e.concurrency+" \\"," --n-gpu-layers auto --flash-attn auto \\"," --threads "+a+" --threads-batch "+e.cores+" \\"," --batch-size "+n+" --ubatch-size "+r],o&&(i[i.length-1]+=" \\",i.push(" --cache-type-k q8_0 --cache-type-v q8_0")),i.join("\n");if("freetoken"===t.runtime)return["ft serve \\",' --model "'+e.model+'" \\'," --max-seq-len-override "+e.context+" \\"," --max-running-requests "+e.concurrency].join("\n");i=['vllm serve "'+e.model+'" \\'," --dtype auto --gpu-memory-utilization 0.90 \\"," --max-model-len "+e.context+" --max-num-seqs "+e.concurrency+" \\"," --tensor-parallel-size "+e.gpuCount],o&&(i[i.length-1]+=" \\",i.push(" --kv-cache-dtype fp8"));const l=Math.ceil(t.offload/e.gpuCount);return l>0&&(i[i.length-1]+=" \\",i.push(" --cpu-offload-gb "+l)),i.join("\n")}(e,u),s=[],v={llama:"llama.cpp",vllm:"vLLM",freetoken:"FreeToken"}[u.runtime];u.directFit?s.push({level:"ok",title:"预计可全量放入 GPU",text:"权重、KV Cache 和基础运行开销合计约 "+m(u.gpuNeed)+" GiB,低于 90% 显存预算。"}):s.push({level:u.ramFit?"warn":"danger",title:"需要 CPU / 内存卸载",text:"超出 GPU 安全预算约 "+m(u.offload)+" GiB。卸载会明显受内存带宽和 PCIe 带宽影响。"}),u.ramFit||s.push({level:"danger",title:"系统内存不足",text:"估算至少需要 "+m(u.ramNeed)+" GiB 可用内存,且不应依赖交换分区维持推理。"}),u.diskFit||s.push({level:"danger",title:"磁盘空间不足",text:"模型、临时文件和格式转换建议至少预留 "+m(u.diskNeed)+" GiB。"}),"llama"===u.runtime&&"gguf"!==e.modelFormat&&s.push({level:"danger",title:"格式与 llama.cpp 不匹配",text:"该后端优先使用 GGUF;请下载对应 GGUF 或先转换模型。"}),"gguf"!==e.modelFormat&&/\.gguf(?:$|[?#])/i.test(e.model)&&s.push({level:"warn",title:"模型路径与格式选择不一致",text:"当前路径看起来是 GGUF 文件,请修改模型格式或填写对应 checkpoint 目录 / Hugging Face ID。"}),"freetoken"===u.runtime&&"gguf"===e.modelFormat&&s.push({level:"danger",title:"FreeToken 不直接使用 GGUF",text:"请改用受支持的 Hugging Face Safetensors checkpoint,或转换为 FTW。"}),"freetoken"===u.runtime&&"nvidia"!==e.gpu.vendor&&s.push({level:"danger",title:"FreeToken 硬件不兼容",text:"当前方案要求 Linux x86_64 与受支持的 NVIDIA CUDA 环境。"}),"freetoken"===u.runtime&&s.push({level:"info",title:"量化必须来自实际权重",text:"表单量化仅用于预算估算;FreeToken 会读取 checkpoint 的真实 dtype,不会把任意 BF16 权重自动变成所选量化。"}),"freetoken"===u.runtime&&"moe"!==e.architecture&&s.push({level:"info",title:"FreeToken 更适合 MoE 卸载",text:"Dense 模型通常优先比较 llama.cpp 或 vLLM;FreeToken 的主要优势是专家缓存与 CPU-GPU 协同。"}),"vllm"===u.runtime&&"nvidia"!==e.gpu.vendor&&"amd"!==e.gpu.vendor&&s.push({level:"danger",title:"当前硬件不适合该 vLLM 命令",text:"Apple Silicon 建议使用 llama.cpp / MLX;自定义设备需核对 vLLM 平台支持。"}),e.context>=32768&&s.push({level:"warn",title:"长上下文成本高",text:"KV Cache 近似随上下文和并发线性增长;模型原生上下文不足时还需要单独验证 RoPE 扩展质量。"}),"q3"===e.quant&&s.push({level:"warn",title:"Q3 量化质量风险",text:"极低比特量化适合先验证能否运行,不应默认视为质量最优配置。"}),"moe"===e.architecture&&s.push({level:"info",title:"MoE 仍需存放全部权重",text:"每个 token 只激活约 "+m(e.activeParams)+"B 参数不代表只需加载这些权重;总参数量决定主要存储与内存需求。"});const p=t("[data-verdict]");var g;p.className="tool-llm-verdict tool-llm-verdict--"+u.level,p.replaceChildren(c("span","",u.directFit?"GPU 直载":u.ramFit&&u.diskFit?"混合卸载":"配置不足"),c("strong","","推荐 "+v),c("small","",e.gpu.name+" × "+e.gpuCount+" · "+r[e.quant]+" · "+m(e.context/1024)+"K 上下文")),t("[data-metrics]").replaceChildren(...(g=[{label:"模型权重",value:m(u.weights)+" GiB"},{label:"KV Cache",value:m(u.kv)+" GiB"},{label:"GPU 总需求",value:m(u.gpuNeed)+" GiB"},{label:"GPU 安全预算",value:m(u.usableVram)+" GiB"},{label:"CPU 卸载",value:m(u.offload)+" GiB"}],g.map((e=>{const t=c("div","tool-metric");return t.append(c("span","",e.label),c("strong","",e.value)),t})))),t("[data-command]").textContent=d,t("[data-findings]").replaceChildren(...(e=>e.map((e=>{const t=c("div","tool-finding tool-finding--"+e.level);return t.append(c("strong","",e.title),document.createTextNode(e.text)),t})))(s)),t("[data-assumptions]").textContent="权重按 "+r[e.quant]+" 的常见封装开销估算;KV Cache 根据参数规模、"+m(e.context/1024)+"K 上下文和 "+e.concurrency+" 并发近似计算。不同 GQA/MLA 层数与后端实现可能产生 30% 以上偏差。",o="大模型本地推理方案\n\n推荐后端:"+v+"\n模型权重:约 "+m(u.weights)+" GiB\nKV Cache:约 "+m(u.kv)+" GiB\nGPU 总需求:约 "+m(u.gpuNeed)+" GiB\nCPU 卸载:约 "+m(u.offload)+" GiB\n\n启动命令:\n"+d+"\n\n限制:\n"+s.map((e=>"- "+e.title+":"+e.text)).join("\n"),t("[data-results]").hidden=!1,t("[data-empty]").hidden=!0}t('[data-config="gpu"]').addEventListener("change",(e=>{const n=a[e.currentTarget.value];"custom"!==e.currentTarget.value&&(t('[data-config="vram"]').value=n.vram)})),t('[data-config="architecture"]').addEventListener("change",(e=>{t("[data-active-field]").hidden="moe"!==e.currentTarget.value})),t('[data-action="sample"]').addEventListener("click",(()=>{Object.entries({runtime:"freetoken",gpu:"rtx3060-6",gpuCount:1,vram:6,ram:64,cores:12,disk:100,architecture:"moe",params:35,activeParams:3,quant:"bf16",format:"safetensors",context:8192,concurrency:1,model:"/models/Qwen3.6-35B-A3B"}).forEach((([e,a])=>{t('[data-config="'+e+'"]').value=a})),t("[data-active-field]").hidden=!1,u()})),t('[data-action="generate"]').addEventListener("click",u),t('[data-action="copy"]').addEventListener("click",(async e=>{if(!o)return;await navigator.clipboard.writeText(o);const t=e.currentTarget.textContent;e.currentTarget.textContent="已复制",setTimeout((()=>{e.currentTarget.textContent=t}),1200)})),u()}();