Spaces:
Running
Running
Download app.js from PabloRod11/pocket-phd: direct link, hf CLI and curl.
- Browser
- Download file 14.3 kB
-
https://huggingface.co/spaces/PabloRod11/pocket-phd/resolve/main/app.js
- Command line
-
hf download hf://spaces/PabloRod11/pocket-phd/app.js
-
curl -L -o app.js https://huggingface.co/spaces/PabloRod11/pocket-phd/resolve/main/app.js
14.3 kB
| ; | |
| const $ = id => document.getElementById(id); | |
| let currentRun = null, investigationController = null; | |
| const array = value => Array.isArray(value) ? value : []; | |
| const printable = value => typeof value === 'string' ? value : JSON.stringify(value, null, 2); | |
| function element(tag, content, className) { const node = document.createElement(tag); if(content !== undefined) node.textContent = printable(content); if(className) node.className = className; return node; } | |
| const storageKey = 'pocket-phd-static-runs'; | |
| function saveRun(run, runs) { | |
| try {localStorage.setItem(storageKey,JSON.stringify([run,...runs].slice(0,50)));} | |
| catch {run.storage_warning='Browser storage is unavailable or full. This result is not saved; export it before leaving this page.';} | |
| return run; | |
| } | |
| function savedRuns() { try { return JSON.parse(localStorage.getItem(storageKey) || '[]'); } catch { throw new Error('Browser notebook storage is unavailable.'); } } | |
| async function request(path, options={}) { | |
| if(path==='/api/config') return {live_available:Boolean(navigator.gpu),model:'Local WebGPU model — load before running'}; | |
| let runs; | |
| try {runs=savedRuns();} catch(error) {if(path!=='/api/research') throw error;runs=[];} | |
| if(path==='/api/research') { | |
| const input=JSON.parse(options.body); | |
| if(input.mode==='live' && !window.PocketResearch.ready()) throw new Error('Load the free local AI first.'); | |
| const evidence=await window.PocketResearch.prepareEvidence(input); | |
| input.sources=evidence.sources; | |
| if(input.mode==='live') { | |
| if(!evidence.sources.length) throw new Error('Local AI needs evidence. Enable Crossref retrieval or paste source records with excerpts.'); | |
| investigationController=new AbortController();$('stop-run').hidden=false; | |
| const run=await window.PocketResearch.investigate(input,evidence, message=>status(message,'pending'),investigationController.signal); | |
| run.id=crypto.randomUUID();run.created_at=new Date().toISOString(); | |
| return saveRun(run,runs); | |
| } | |
| if(input.sources.length>20) throw new Error('Use at most 20 evidence records.'); | |
| const response=await fetch('./demo-template.json'); | |
| if(!response.ok) throw new Error('Demo template could not be loaded.'); | |
| const template=await response.json(); | |
| const substitute=value=> typeof value==='string' ? value.replaceAll('POCKET_RESEARCH_TOPIC',input.question.replace(/[ ?.]+$/,'')) : Array.isArray(value) ? value.map(substitute) : value && typeof value==='object' ? Object.fromEntries(Object.entries(value).map(([key,item])=>[key,substitute(item)])) : value; | |
| const run=substitute(template); | |
| run.question=input.question;run.id=crypto.randomUUID();run.created_at=new Date().toISOString(); | |
| run.sources=input.sources;run.retrieval=evidence.retrieval; | |
| run.evidence_status=run.sources.length ? 'supplied records only' : 'no evidence'; | |
| if(run.sources.length) { | |
| run.limitations=run.limitations.filter(item=>!item.startsWith('No evidence was supplied.')); | |
| run.gaps.forEach(gap=>gap.evidence_status='demo template; no source entailment claimed'); | |
| run.agents[2].output.gaps.forEach(gap=>gap.evidence_status='demo template; no source entailment claimed'); | |
| } | |
| run.agents[0].output.research_question=input.question; | |
| const scout=run.agents[1].output; | |
| scout.synthesis=`${run.sources.length} supplied records are available. This demo counts records and does not interpret or verify findings.`; | |
| scout.citations=run.sources.map(source=>source.id); | |
| run.limitations.push(...evidence.limitations,'Method demo uses templates. Select Local AI for model-generated proposals. Notes are stored in this browser.'); | |
| return saveRun(run,runs); | |
| } | |
| if(path==='/api/runs') return runs; | |
| const id=decodeURIComponent(path.split('/').pop()); | |
| const run=runs.find(item=>item.id===id); | |
| if(!run) throw new Error('Investigation not found in this browser.'); | |
| if(options.method==='DELETE') {localStorage.setItem(storageKey,JSON.stringify(runs.filter(item=>item.id!==id)));return {deleted:true};} | |
| return run; | |
| } | |
| function status(message, type='') { const node = $('status'); node.hidden = false; node.className = type; node.textContent = message; } | |
| function fields(parent, data, excluded=[]) { const list = element('dl'); Object.entries(data || {}).forEach(([key, value]) => { if(excluded.includes(key) || value === null || value === undefined) return; list.append(element('dt', key.replaceAll('_', ' ')), element('dd', Array.isArray(value) ? value.map(printable).join('\n') : printable(value))); }); parent.append(list); } | |
| function card(data, index, label) { const node=element('article', undefined, 'card'); node.append(element('span', `${label} ${String(index+1).padStart(2,'0')}`, 'tag'), element('h4', data.title || data.name || `${label} ${index+1}`)); return node; } | |
| function renderGaps(run) { const target=$('gaps'); target.replaceChildren(); array(run.gaps).forEach((gap,index)=> { const node=card(gap,index,'Gap'); if(gap.description) node.append(element('p',gap.description)); fields(node,gap,['id','title','description']); target.append(node); }); if(!target.childNodes.length) target.append(element('p','No knowledge gaps returned for this run.','empty')); } | |
| function renderHypotheses(run) { const target=$('hypotheses'); target.replaceChildren(); array(run.hypotheses).forEach((hypothesis,index)=> { const node=card(hypothesis,index,'Hypothesis'); fields(node,hypothesis,['id','title','gap_id']); const plans=array(run.experiments).filter(plan=>plan.hypothesis_id===hypothesis.id); if(plans.length){ const details=element('details'); details.append(element('summary','Explore the test plan')); plans.forEach(plan=>{const section=element('section'); section.append(element('h4',plan.title || 'Experiment'));fields(section,plan,['id','hypothesis_id','title']);details.append(section);});node.append(details);} target.append(node); }); if(!target.childNodes.length) target.append(element('p','No hypotheses returned for this run.','empty')); } | |
| function safeURL(value) { try {const url=new URL(value); return ['https:','http:'].includes(url.protocol) ? url.href : null;}catch{return null;} } | |
| function renderSources(run) { const target=$('evidence');target.replaceChildren();if(run.retrieval) {const info=element('article',undefined,'notice');info.append(element('h3','Literature search'));fields(info,run.retrieval);target.append(info);}array(run.sources).forEach((source,index)=>{const node=card(source,index,source.id || 'Source');node.classList.add('source-card');node.append(element('p',[source.year,Array.isArray(source.authors)?source.authors.join(', '):source.authors].filter(Boolean).join(' · '),'meta'));if(source.abstract)node.append(element('p',source.abstract));if(source.provenance)node.append(element('p',`Provenance: ${printable(source.provenance)}`,'meta'));const url=safeURL(source.url);if(url){const link=element('a',source.url,'source-link');link.href=url;link.target='_blank';link.rel='noopener noreferrer';node.append(link);}else if(source.url)node.append(element('p',source.url));target.append(node);});if(!array(run.sources).length)target.append(element('p','No sources were returned. Findings without source support should be treated as exploratory proposals.','empty')); } | |
| function renderAgents(run) {const target=$('agents');target.replaceChildren();array(run.agents).forEach((agent,index)=>{const node=card(agent,index,'Agent');node.append(element('span',agent.status || 'Recorded','tag'),element('pre',agent.error || printable(agent.output) || 'No output recorded.','agent-output'));target.append(node);});if(!target.childNodes.length)target.append(element('p','No agent outputs were returned.','empty'));} | |
| function showRun(run) {currentRun=run;$('results').hidden=false;$('results-title').textContent=run.question || 'Research investigation';$('run-mode').textContent=run.mode==='demo'?'METHOD DEMO · ILLUSTRATIVE OUTPUT':'LIVE INVESTIGATION · NOVELTY UNVERIFIED';$('result-notice').textContent=run.mode==='demo'?'This is an illustrative method demonstration. Its deterministic hypotheses are not validated research findings or a comprehensive review.':'Model-generated proposals require source verification, domain review, and replication. Novelty has not been established.';if(run.status && run.status!=='complete'){$('result-notice').textContent=`Investigation ${run.status}. ${run.error || 'Some agent stages did not complete. Review the agent notebook for details.'}`;$('result-notice').classList.add('run-failure');}else{$('result-notice').classList.remove('run-failure');}renderGaps(run);renderHypotheses(run);renderSources(run);renderAgents(run);$('limitations').replaceChildren();array(run.limitations).forEach(limit=>$('limitations').append(element('p',limit)));if(!array(run.limitations).length)$('limitations').append(element('p','No run-specific limitations were returned. This tool cannot establish novelty or validate causal claims.'));$('results').scrollIntoView({behavior:'smooth',block:'start'});} | |
| async function history() {try{const runs=await request('/api/runs');const target=$('history-list');target.replaceChildren();runs.forEach(run=>{const button=element('button',run.question || run.id,'history-item');button.type='button';button.append(element('small',[run.mode,run.created_at ? new Date(run.created_at).toLocaleString():null].filter(Boolean).join(' · ')));button.onclick=async()=>{try{showRun(await request(`/api/runs/${encodeURIComponent(run.id)}`));}catch(error){status(error.message,'error');}};target.append(button);});if(!runs.length)target.append(element('p','Your investigations will appear here.','hint'));}catch{$('history-list').replaceChildren(element('p','Research history is temporarily unavailable.','hint'));}} | |
| $('research-form').addEventListener('submit',async event=>{event.preventDefault();let sources=[];try{const pasted=$('sources').value.trim();if(pasted){sources=JSON.parse(pasted);if(!Array.isArray(sources))throw new Error('Sources must be a JSON array.');if(sources.some(source=>!source || typeof source!=='object' || typeof source.title!=='string' || !source.title.trim()))throw new Error('Each source needs a title. URLs and excerpts are optional.');}}catch(error){status(`Check your evidence: ${error.message}`,'error');return;}const question=$('question').value.trim();if(!question){status('Enter a research question.','error');return;}const mode=document.querySelector('input[name="mode"]:checked').value;$('run-button').disabled=true;$('research-form').setAttribute('aria-busy','true');status(mode==='live'?'The six-agent investigation is running. Live research may take several minutes; stage outputs will appear when the investigation completes.':'Running the six-role browser demonstration…','pending');try{const run=await request('/api/research',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({question,mode,sources,retrieve:$('retrieve').checked,search_query:$('search-query').value.trim()})});showRun(run);status(run.storage_warning || (run.status==='failed'?'The investigation failed. Review the saved agent notebook for details.':run.status==='partial'?'A partial investigation was saved. Review incomplete stages in the agent notebook.':'Investigation saved to your research notebook.'),run.storage_warning || run.status==='failed'?'error':'');await history();}catch(error){status(error.message,'error');}finally{investigationController=null;$('stop-run').hidden=true;$('run-button').disabled=false;$('research-form').removeAttribute('aria-busy');}}); | |
| $('example').onclick=()=>{$('question').value='How might retrieval practice and sleep timing interact to improve long-term learning in university students?';$('search-query').value='retrieval practice sleep memory';$('question').focus();}; | |
| $('refresh-history').onclick=history; | |
| document.querySelectorAll('[data-tab]').forEach(button=>button.onclick=()=>{document.querySelectorAll('[data-tab]').forEach(item=>{if(item===button)item.setAttribute('aria-current','page');else item.removeAttribute('aria-current');});document.querySelectorAll('.tab-panel').forEach(panel=>panel.hidden=panel.id!==button.dataset.tab);}); | |
| function download(extension){ | |
| if(!currentRun)return; | |
| const content=extension==='json'?JSON.stringify(currentRun,null,2):`# ${currentRun.question}\n\n${currentRun.mode==='demo'?'Illustrative method demo':'Local AI proposals'}. Novelty unverified.\n\n`+['gaps','hypotheses','experiments','sources','agents','limitations'].map(key=>`## ${key}\n\n`+JSON.stringify(currentRun[key],null,2)).join('\n\n'); | |
| const url=URL.createObjectURL(new Blob([content],{type:extension==='json'?'application/json':'text/markdown'})); | |
| const link=element('a');link.href=url;link.download=`pocket-phd-${currentRun.id}.${extension}`;document.body.append(link);link.click();link.remove();URL.revokeObjectURL(url); | |
| } | |
| $('delete-run').onclick=async()=>{if(!currentRun || !confirm('Delete this saved investigation? This cannot be undone.'))return;try{await request(`/api/runs/${encodeURIComponent(currentRun.id)}`,{method:'DELETE',headers:{'X-Pocket-PhD':'local'}});currentRun=null;$('results').hidden=true;status('Investigation deleted.');await history();}catch(error){status(error.message,'error');}}; | |
| $('export-json').onclick=()=>download('json'); | |
| $('export-md').onclick=()=>download('md'); | |
| (async()=>{try{const config=await request('/api/config');document.querySelector('input[value="live"]').disabled=!config.live_available;$('live-label').classList.toggle('unavailable',!config.live_available);$('live-note').textContent=config.live_available?`Configured: ${config.model || 'research model'}`:'WebGPU unavailable — try Chrome or Edge';}catch{$('live-note').textContent='Model configuration unavailable';}await history();})(); | |
| $('load-model').onclick=async()=>{ $('load-model').disabled=true; try {await window.PocketResearch.load(message=>{$('model-status').textContent=message;}); $('live-note').textContent='Model loaded — runs on this device';document.querySelector('input[value="live"]').disabled=false;} catch(error){$('model-status').textContent=error.message;} finally{$('load-model').disabled=false;} }; | |
| $('stop-run').onclick=()=>{investigationController?.abort();$('live-note').textContent='Stopped — reload the local model before another AI run';}; | |