Download src/selftest.js from ProCreations/minicpm5-2b-webgpu: direct link, hf CLI and curl.
- Browser
- Download file 13.4 kB
-
https://huggingface.co/spaces/ProCreations/minicpm5-2b-webgpu/resolve/main/src/selftest.js
- Command line
-
hf download hf://spaces/ProCreations/minicpm5-2b-webgpu/src/selftest.js
-
curl -L -o selftest.js https://huggingface.co/spaces/ProCreations/minicpm5-2b-webgpu/resolve/main/src/selftest.js
13.4 kB
| import {DEFAULTS,TOOLS,SYSTEM,parseXMLCalls} from './core.js'; | |
| import {runAgent} from './agent.js'; | |
| export async function runSelfTest({engine,settings,createArtifact,openArtifact,pauseScene}) { | |
| const p=document.createElement('div');p.id='selftest';p.style='position:fixed;top:75px;right:20px;max-width:620px;max-height:80vh;overflow:auto;z-index:99;background:#151a2a;color:#d5e0ff;border:1px solid #6572ac;padding:20px;font:12px monospace;border-radius:12px';p.innerHTML='<h2>Browser verification</h2><button id="test-artifacts">Test four file formats</button> <button id="test-inference">Test Q8 GPU inference & tools</button> <button id="test-warm">Test warm load</button> <button id="test-session">Test session-only loading</button> <button id="test-benchmark">Benchmark runtime settings</button><pre id="test-output" style="white-space:pre-wrap"></pre><button id="test-close">Close</button>';document.body.append(p); | |
| const output=p.querySelector('#test-output');const report=[];const log=(name,data)=>{report.push({name,data});output.textContent+=name+' '+JSON.stringify(data)+'\n';fetch(new URLSearchParams(location.search).has('qa-weights')?'/__qa/report-local':'/__qa/report',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(report)}).catch(()=>{});}; | |
| p.querySelector('#test-close').onclick=()=>p.remove(); | |
| const repair=document.createElement('button');repair.textContent='Test automatic tool repair';p.insertBefore(repair,output); | |
| repair.onclick=async()=>{try{if(!engine.ready)await engine.load({...DEFAULTS,saveModel:false});pauseScene();const history=[{role:'user',content:'Create an awesome looking black hole in HTML with an animated accretion disk. Use create_html, and keep the complete single file under 3000 tokens.'}];const events=[];await runAgent({chat:engine.chat.bind(engine),history,settings:{...DEFAULTS,maxTokens:128,temperature:0},signal:new AbortController().signal,createArtifact:async(name,args)=>{const file=await createArtifact(name,args);await fetch('/__qa/file/repair-'+file.filename,{method:'POST',body:file.blob});return file;},onEvent:async event=>{if(event.type!=='chunk'){events.push({...event,artifact:event.artifact?{filename:event.artifact.filename,bytes:event.artifact.blob.size}:undefined});log('repair event',events.at(-1));}}});await fetch('/__qa/file/agent-repair.json',{method:'POST',body:JSON.stringify({events,history})});}catch(error){log('repair error',{message:error.message,stack:error.stack});}}; | |
| const verify=document.createElement('button');verify.textContent='Verify dedicated runtime';p.insertBefore(verify,output); | |
| verify.onclick=async()=>{try{ | |
| if(!engine.ready)await engine.load({...DEFAULTS,saveModel:false});pauseScene(); | |
| const results=[]; | |
| for(const prompt of ['<s><|im_start|>user\nWhat is 23 times 7? Answer in one sentence.<|im_end|>\n<|im_start|>assistant\n<think>\n\n</think>\n\n','<s><|im_start|>user\nWrite a JavaScript function that returns the factorial of a nonnegative integer, and explain it.<|im_end|>\n<|im_start|>assistant\n<think>\n\n</think>\n\n','<s><|im_start|>user\nExplain photosynthesis in at least 400 words, with a detailed description of each stage.<|im_end|>\n<|im_start|>assistant\n<think>\n\n</think>\n\n']){ | |
| const tokens=[];let last;await engine.dedicated.generate(prompt,{...DEFAULTS,maxTokens:256,temperature:0},new AbortController().signal,item=>{if(item.token!==undefined){tokens.push(item.token);last=item;}}); | |
| results.push({prompt,tokens,text:last?.text,prefillMs:last?.prefillMs,seconds:last?.elapsed/1000,tps:(tokens.length-1)/((last.elapsed-last.prefillMs)/1000),subgroups:engine.dedicated.subgroups,speculation:engine.dedicated.specStats});log('dedicated verification',results.at(-1)); | |
| } | |
| await fetch('/__qa/file/dedicated-verification.json',{method:'POST',body:JSON.stringify(results)}); | |
| }catch(error){log('dedicated error',{message:error.message,stack:error.stack});}}; | |
| const coverage=document.createElement('button');coverage.textContent='Verify runtime coverage';p.insertBefore(coverage,output); | |
| coverage.onclick=async()=>{const results=[];try{ | |
| const prompt='<s><|im_start|>user\nWrite a JavaScript function that returns the factorial of a nonnegative integer, and explain it.<|im_end|>\n<|im_start|>assistant\n<think>\n\n</think>\n\n'; | |
| const configs=[{name:'base-f16',dspark:'off'},{name:'auto-f16',dspark:'auto'},{name:'sample-base',dspark:'off',temperature:1},{name:'sample-auto',dspark:'auto',temperature:1},{name:'q8-64k',cacheK:'q8_0',cacheV:'q8_0',context:65536,dspark:'auto'},{name:'q4-128k',cacheK:'q4_0',cacheV:'q4_0',context:131072,dspark:'auto'},{name:'f32',cacheK:'f32',cacheV:'f32',dspark:'off'},{name:'full-vocabulary',topK:0,temperature:1,dspark:'off'}]; | |
| for(const config of configs){const options={...DEFAULTS,temperature:0,maxTokens:128,seed:1234,...config}; | |
| if(!engine.ready||engine.settings.cacheK!==options.cacheK||engine.settings.context!==options.context||(!engine.dedicated.draft&&options.dspark!=='off')){await engine.unload();await engine.load({...options,saveModel:false});pauseScene();} | |
| const tokens=[];let last;await engine.dedicated.generate(prompt,options,new AbortController().signal,item=>{if(item.token!==undefined){tokens.push(item.token);last=item;}}); | |
| const result={config,tokens,text:last?.text,tps:(tokens.length-1)/((last.elapsed-last.prefillMs)/1000),prefillMs:last.prefillMs,subgroups:engine.dedicated.subgroups,sharded:engine.dedicated.sharded,speculation:engine.dedicated.specStats};results.push(result);log('coverage',result); | |
| } | |
| await fetch('/__qa/file/runtime-coverage.json',{method:'POST',body:JSON.stringify(results)});log('coverage complete',true); | |
| }catch(error){log('coverage error',{message:error.message,stack:error.stack});}}; | |
| const sustain=document.createElement('button');sustain.textContent='Stress 64K FP16 + DSpark';p.insertBefore(sustain,output); | |
| sustain.onclick=async()=>{try{ | |
| await engine.unload();const options={...DEFAULTS,context:65536,cacheK:'f16',cacheV:'f16',dspark:'on',temperature:0,maxTokens:4096,saveModel:false};await engine.load(options);pauseScene(); | |
| const prompt='<s><|im_start|>user\nWrite a complete, detailed, self-contained HTML space simulator with canvas animation, planet controls, pause, zoom, orbit trails, keyboard controls, and an explanation of the implementation. Include all code inline.<|im_end|>\n<|im_start|>assistant\n<think>\n\n</think>\n\n';const tokens=[];let last; | |
| for(let round=0;round<3;round++){await engine.dedicated.generate(prompt,options,new AbortController().signal,item=>{if(item.token!==undefined){tokens.push(item.token);last=item;}});log('64K stress round',{round,tokens:last.index,tps:(last.index-1)/((last.elapsed-last.prefillMs)/1000),speculation:engine.dedicated.specStats});} | |
| await fetch('/__qa/file/safari-stream-fix-64k.json',{method:'POST',body:JSON.stringify({tokens:tokens.length,context:65536,kv:'f16',dspark:'on',lastText:last.text,report})});log('64K stress complete',tokens.length); | |
| }catch(error){log('64K stress error',{message:error.message,stack:error.stack});}}; | |
| const snapshot=document.createElement('button');snapshot.textContent='Inspect recovery checkpoint';p.insertBefore(snapshot,output);snapshot.onclick=async()=>{const {loadSessions}=await import('./storage.js');const sessions=await loadSessions();const checkpoint=sessions.find(s=>s.pendingGeneration)||sessions[0];const summary=checkpoint?{id:checkpoint.id,pendingGeneration:checkpoint.pendingGeneration,updatedAt:checkpoint.updatedAt,messages:checkpoint.messages.map(m=>({role:m.role,generating:m.generating,characters:m.content?.length,artifacts:m.artifacts?.length}))}:null;log('checkpoint',summary);await fetch('/__qa/file/recovery-checkpoint.json',{method:'POST',body:JSON.stringify(summary)});}; | |
| const samples=[['create_html',{title:'Orbit QA',html:'<!DOCTYPE html><html><head><style>body{font-family:system-ui;padding:40px;background:#101a37;color:white}button{padding:15px;background:#999aff;border:0}</style></head><body><h1>Orbit studio</h1><button onclick="this.textContent=\'Works!\'">Try interaction</button></body></html>'}],['create_document',{title:'Launch Plan',markdown:'# Objective\nCreate something useful.\n\n## Checklist\n- Design\n- Build\n- Verify\n\n| Phase | Status |\n| --- | --- |\n| Build | Done |'}],['create_spreadsheet',{title:'Budget',sheets:[{name:'Plan',columns:['Item','Cost'],rows:[['Design',120],['Build',240],['Total','=SUM(B2:B3)']]}]}],['create_pdf',{title:'Field Notes',markdown:'# A new perspective\nA practical guide with Unicode: café, naïve, résumé, π, € and —.\n\n## Checklist\n- Take your time.\n- Make something thoughtful.\n\n'+Array.from({length:80},(_,i)=>`Paragraph ${i+1}. Observe, reflect and keep a record of what you discover.\n\n`).join('')}]]; | |
| p.querySelector('#test-artifacts').onclick=async()=>{for(const [name,args]of samples){try{const a=await createArtifact(name,args);log(name,{size:a.blob.size,type:a.blob.type});await fetch('/__qa/file/'+a.filename,{method:'POST',body:a.blob});const b=document.createElement('button');b.textContent='Preview '+a.type;b.onclick=()=>openArtifact(a);p.insertBefore(b,output);}catch(e){log(name,{error:e.stack});}}}; | |
| const runPrompt=async(prompt,maxTokens=1024)=>{ | |
| let content='',reasoning='',t=null,calls=new Map(),finish='',usage=null,first=0;const start=performance.now(); | |
| await engine.chat([{role:'system',content:SYSTEM},{role:'user',content:prompt}],{...DEFAULTS,context:settings.context,maxTokens,thinking:false,temperature:.2},TOOLS,new AbortController().signal, ch=> { | |
| const d=ch.choices?.[0]?.delta||{};if((d.content||d.reasoning_content||d.tool_calls)&&!first)first=performance.now();content+=d.content||'';reasoning+=d.reasoning_content||'';if(ch.timings)t=ch.timings;if(ch.usage)usage=ch.usage;if(ch.choices?.[0]?.finish_reason)finish=ch.choices[0].finish_reason; | |
| for(const tc of d.tool_calls||[]){let c=calls.get(tc.index)||{id:tc.id||'test',type:'function',function:{name:'',arguments:''}};c.function.name+=tc.function?.name||'';c.function.arguments+=tc.function?.arguments||'';calls.set(tc.index,c);} | |
| }); | |
| const result={prompt,content,reasoning,calls:calls.size?[...calls.values()]:parseXMLCalls(content),timings:t,usage,finish,seconds:(performance.now()-start)/1000,ttft:(first-start)/1000};log('inference',result); | |
| if(engine.gpuShaders)await fetch('/__qa/file/shaders.json',{method:'POST',body:JSON.stringify(engine.gpuShaders)}); | |
| for(const c of result.calls){try{const a=await createArtifact(c.function.name,JSON.parse(c.function.arguments));await fetch('/__qa/file/model-'+a.filename,{method:'POST',body:a.blob});log('model artifact',{tool:c.function.name,size:a.blob.size});}catch(e){log('model artifact error',e.message);}} | |
| }; | |
| p.querySelector('#test-inference').onclick=async()=>{try{log('start',await engine.probe());if(!engine.ready)await engine.load({...DEFAULTS,...settings});log('loaded',{seconds:engine.loadSeconds,context:engine.info?.n_ctx,gpuLayers:engine.gpuLayers,logs:engine.logs});await runPrompt('Reply with one sentence: what is 12 times 12?',128);for(const prompt of ['Use create_html to build a tiny complete HTML page with a purple background, a heading Hello MiniCPM, and a button that changes the heading when clicked.','Use create_document to create a short Word document titled Three Launch Steps with a heading and three bullet points.','Use create_spreadsheet to create a budget workbook with one sheet, columns Item and Cost, rows Design 100, Build 200, and Total with a SUM formula.','Use create_pdf to create a short PDF called Moon Notes with two sections about observing the Moon.'])await runPrompt(prompt,1500);log('GPU and tools complete',true);}catch(e){log('ERROR',{message:e.message,stack:e.stack,logs:engine.logs});}}; | |
| p.querySelector('#test-benchmark').onclick=async()=>{for(const config of [{name:'fp16-repeat',cacheK:'f16',cacheV:'f16'},{name:'q8-repeat',cacheK:'q8_0',cacheV:'q8_0'},{name:'fp16-four-threads',cacheK:'f16',cacheV:'f16',benchmarkOptions:{n_threads:4}}]){try{await engine.unload();await engine.load({...DEFAULTS,saveModel:true,...config});pauseScene();log('benchmark config',config);await runPrompt('Explain photosynthesis in at least 400 words, with a detailed description of each stage. Respond in plain text and do not call any tool.',256);}catch(e){log('benchmark error',{config,message:e.message});}}log('benchmark complete',true);}; | |
| p.querySelector('#test-session').onclick=async()=>{try{await engine.unload();await engine.clearCache();const before=await engine.cacheState();await engine.load({...DEFAULTS,saveModel:false});const after=await engine.cacheState();if(before.bytes||after.bytes)throw new Error('Session-only load wrote persistent model weights');log('session-only load',{seconds:engine.loadSeconds,gpuLayers:engine.gpuLayers,bytesBefore:before.bytes,bytesAfter:after.bytes});await runPrompt('Reply with only the answer: what is 12 times 12?',128);}catch(e){log('session error',{message:e.message,stack:e.stack,logs:engine.logs});}}; | |
| p.querySelector('#test-warm').onclick=async()=>{try{await engine.unload();await engine.load({...DEFAULTS,...settings,saveModel:true});log('warm load',{seconds:engine.loadSeconds,gpuLayers:engine.gpuLayers});await runPrompt('Write a concise paragraph about why the sky is blue.',256);}catch(e){log('warm error',{message:e.message,logs:engine.logs});}}; | |
| } | |