Spaces:
Running
Running
Require evidence for local AI and reject unsupported citation claims
Browse files- README.md +1 -1
- app.js +1 -0
- index.html +1 -1
- research.js +2 -1
README.md
CHANGED
|
@@ -15,7 +15,7 @@ Choose **Load free local AI** to download Qwen2.5 1.5B via WebLLM (roughly 1 GB;
|
|
| 15 |
|
| 16 |
Optional scholarly retrieval sends your question or keywords to the free Crossref API and returns up to eight metadata records, with abstracts where available. No full-text systematic review is performed.
|
| 17 |
|
| 18 |
-
Select **Local AI**, then start an investigation. Small-model structured-output failures preserve completed stages and mark the result partial or failed. The deterministic method demo remains available.
|
| 19 |
|
| 20 |
Notes stay in this browser and can be exported. Browser storage can be cleared. Context is bounded to six records and 800 characters per excerpt; the notebook retains complete evidence. Citation checks validate IDs, not claim support. All novelty is unverified. Expert review remains necessary.
|
| 21 |
|
|
|
|
| 15 |
|
| 16 |
Optional scholarly retrieval sends your question or keywords to the free Crossref API and returns up to eight metadata records, with abstracts where available. No full-text systematic review is performed.
|
| 17 |
|
| 18 |
+
Select **Local AI**, enable Crossref retrieval or paste evidence, then start an investigation. Local AI requires at least one evidence record. Small-model structured-output failures preserve completed stages and mark the result partial or failed. The deterministic method demo remains available.
|
| 19 |
|
| 20 |
Notes stay in this browser and can be exported. Browser storage can be cleared. Context is bounded to six records and 800 characters per excerpt; the notebook retains complete evidence. Citation checks validate IDs, not claim support. All novelty is unverified. Expert review remains necessary.
|
| 21 |
|
app.js
CHANGED
|
@@ -15,6 +15,7 @@ async function request(path, options={}) {
|
|
| 15 |
const evidence=await window.PocketResearch.prepareEvidence(input);
|
| 16 |
input.sources=evidence.sources;
|
| 17 |
if(input.mode==='live') {
|
|
|
|
| 18 |
const run=await window.PocketResearch.investigate(input,evidence, message=>status(message,'pending'));
|
| 19 |
run.id=crypto.randomUUID();run.created_at=new Date().toISOString();
|
| 20 |
localStorage.setItem(storageKey,JSON.stringify([run,...runs].slice(0,50)));return run;
|
|
|
|
| 15 |
const evidence=await window.PocketResearch.prepareEvidence(input);
|
| 16 |
input.sources=evidence.sources;
|
| 17 |
if(input.mode==='live') {
|
| 18 |
+
if(!evidence.sources.length) throw new Error('Local AI needs evidence. Enable Crossref retrieval or paste source records with excerpts.');
|
| 19 |
const run=await window.PocketResearch.investigate(input,evidence, message=>status(message,'pending'));
|
| 20 |
run.id=crypto.randomUUID();run.created_at=new Date().toISOString();
|
| 21 |
localStorage.setItem(storageKey,JSON.stringify([run,...runs].slice(0,50)));return run;
|
index.html
CHANGED
|
@@ -2,6 +2,6 @@
|
|
| 2 |
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Pocket PhD · Research workbench</title><link rel="stylesheet" href="./style.css"><script src="./research.js" defer></script><script src="./app.js" defer></script></head>
|
| 3 |
<body><a class="skip" href="#question">Skip to research question</a><header><a class="brand" href="./" aria-label="Pocket PhD home"><span class="brand-mark" aria-hidden="true">PhD</span><span>Pocket PhD<small>Research notebook</small></span></a><span class="local-tag">Local AI workspace</span></header>
|
| 4 |
<main><section class="intro"><div class="eyebrow">New investigation</div><h1>What would you like to find out?</h1><p>Work from published evidence to candidate hypotheses and a testable plan.</p></section>
|
| 5 |
-
<div class="workspace"><section class="composer panel" aria-labelledby="compose-title"><div class="section-top"><div><h2 id="compose-title">Research question</h2></div><button type="button" id="example" class="text-button">Use example</button></div><form id="research-form"><label class="sr-only" for="question">Research question</label><textarea id="question" required minlength="10" maxlength="2000" rows="4" placeholder="Ask a specific question about a phenomenon, population, or mechanism."></textarea><details class="evidence-input"><summary>Add your own evidence <span>optional</span></summary><label for="sources">Paste sources as a JSON array</label><p class="hint">Each source needs a title. Optional: URL, abstract or excerpt, year, and authors. Your text becomes evidence; the tool does not verify pasted claims.</p><textarea id="sources" rows="6" placeholder='[{"title":"Paper title","url":"https://…","abstract":"Relevant findings…","year":2025}]'></textarea></details><fieldset><legend>Research mode</legend><div class="modes"><label><input type="radio" name="mode" value="demo" checked><span><strong>Method demo</strong><small>Deterministic, illustrative reasoning</small></span></label><label id="live-label" class="unavailable"><input type="radio" name="mode" value="live" disabled><span><strong>Local AI</strong><small id="live-note">Checking browser support…</small></span></label></div></fieldset><div class="local-ai"><button id="load-model" type="button">Load free local AI</button><p id="model-status" class="hint" role="status">Downloads a model to your browser (about 1 GB). Requires WebGPU and sufficient device memory. No API key or paid service.</p></div><label class="retrieve"><input type="checkbox" id="retrieve"> Retrieve scholarly records from Crossref</label><label class="search-label" for="search-query">Literature search terms <span class="hint">optional</span></label><input class="search-query" id="search-query" maxlength="500" placeholder="e.g. retrieval practice sleep memory"><p class="hint">Search terms go to Crossref. Records may lack abstracts; this is not a full-text review. Notes and AI reasoning stay on your device.</p><div class="form-bottom"><p class="hint">Hypotheses are proposals, not discoveries.<br>Novelty requires expert review and a broader literature search.</p><button type="submit" id="run-button" class="primary">Start investigation</button></div></form><div id="status" role="status" aria-live="polite" hidden></div></section><aside class="sidebar"><section class="panel team"><h2>Research method</h2><ol><li><span>01</span><div>Question Framer<small>Define scope & assumptions</small></div></li><li><span>02</span><div>Literature Scout<small>Assemble the evidence</small></div></li><li><span>03</span><div>Gap Analyst<small>Find unanswered questions</small></div></li><li><span>04</span><div>Hypothesis Architect<small>Propose causal explanations</small></div></li><li><span>05</span><div>Skeptic<small>Challenge novelty & inference</small></div></li><li><span>06</span><div>Experiment Designer<small>Make predictions testable</small></div></li></ol></section><section class="history"><div class="section-top"><h3>Research notebook</h3><button id="refresh-history" class="text-button" aria-label="Refresh research history">↻</button></div><div id="history-list"><p class="hint">Loading previous investigations…</p></div></section></aside></div>
|
| 6 |
<section id="results" hidden aria-labelledby="results-title"><div class="results-header"><div><div class="eyebrow" id="run-mode">INVESTIGATION</div><h2 id="results-title"></h2></div><div class="exports"><button id="export-md">Export notes ↓</button><button id="export-json">JSON ↓</button><button id="delete-run">Delete</button></div></div><div id="result-notice" class="notice"></div><nav class="tabs" aria-label="Research results"><button data-tab="findings" aria-current="page">Findings</button><button data-tab="evidence">Evidence library</button><button data-tab="agents">Agent notebook</button></nav><div id="findings" class="tab-panel"><div class="findings-grid"><section><h3>Knowledge gaps</h3><div id="gaps"></div></section><section><h3>Candidate hypotheses</h3><div id="hypotheses"></div></section></div><section class="limits"><h3>Limits of this investigation</h3><div id="limitations"></div></section></div><div id="evidence" class="tab-panel" hidden></div><div id="agents" class="tab-panel" hidden></div></section>
|
| 7 |
<footer><span>Pocket PhD</span><p>Research notebook</p></footer></main></body></html>
|
|
|
|
| 2 |
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Pocket PhD · Research workbench</title><link rel="stylesheet" href="./style.css"><script src="./research.js" defer></script><script src="./app.js" defer></script></head>
|
| 3 |
<body><a class="skip" href="#question">Skip to research question</a><header><a class="brand" href="./" aria-label="Pocket PhD home"><span class="brand-mark" aria-hidden="true">PhD</span><span>Pocket PhD<small>Research notebook</small></span></a><span class="local-tag">Local AI workspace</span></header>
|
| 4 |
<main><section class="intro"><div class="eyebrow">New investigation</div><h1>What would you like to find out?</h1><p>Work from published evidence to candidate hypotheses and a testable plan.</p></section>
|
| 5 |
+
<div class="workspace"><section class="composer panel" aria-labelledby="compose-title"><div class="section-top"><div><h2 id="compose-title">Research question</h2></div><button type="button" id="example" class="text-button">Use example</button></div><form id="research-form"><label class="sr-only" for="question">Research question</label><textarea id="question" required minlength="10" maxlength="2000" rows="4" placeholder="Ask a specific question about a phenomenon, population, or mechanism."></textarea><details class="evidence-input"><summary>Add your own evidence <span>optional</span></summary><label for="sources">Paste sources as a JSON array</label><p class="hint">Each source needs a title. Optional: URL, abstract or excerpt, year, and authors. Your text becomes evidence; the tool does not verify pasted claims.</p><textarea id="sources" rows="6" placeholder='[{"title":"Paper title","url":"https://…","abstract":"Relevant findings…","year":2025}]'></textarea></details><fieldset><legend>Research mode</legend><div class="modes"><label><input type="radio" name="mode" value="demo" checked><span><strong>Method demo</strong><small>Deterministic, illustrative reasoning</small></span></label><label id="live-label" class="unavailable"><input type="radio" name="mode" value="live" disabled><span><strong>Local AI · experimental</strong><small id="live-note">Checking browser support…</small></span></label></div></fieldset><div class="local-ai"><button id="load-model" type="button">Load free local AI</button><p id="model-status" class="hint" role="status">Downloads a model to your browser (about 1 GB). Requires WebGPU and sufficient device memory. No API key or paid service.</p></div><label class="retrieve"><input type="checkbox" id="retrieve"> Retrieve scholarly records from Crossref</label><label class="search-label" for="search-query">Literature search terms <span class="hint">optional</span></label><input class="search-query" id="search-query" maxlength="500" placeholder="e.g. retrieval practice sleep memory"><p class="hint">Search terms go to Crossref. Records may lack abstracts; this is not a full-text review. Notes and AI reasoning stay on your device.</p><div class="form-bottom"><p class="hint">Hypotheses are proposals, not discoveries.<br>Novelty requires expert review and a broader literature search.</p><button type="submit" id="run-button" class="primary">Start investigation</button></div></form><div id="status" role="status" aria-live="polite" hidden></div></section><aside class="sidebar"><section class="panel team"><h2>Research method</h2><ol><li><span>01</span><div>Question Framer<small>Define scope & assumptions</small></div></li><li><span>02</span><div>Literature Scout<small>Assemble the evidence</small></div></li><li><span>03</span><div>Gap Analyst<small>Find unanswered questions</small></div></li><li><span>04</span><div>Hypothesis Architect<small>Propose causal explanations</small></div></li><li><span>05</span><div>Skeptic<small>Challenge novelty & inference</small></div></li><li><span>06</span><div>Experiment Designer<small>Make predictions testable</small></div></li></ol></section><section class="history"><div class="section-top"><h3>Research notebook</h3><button id="refresh-history" class="text-button" aria-label="Refresh research history">↻</button></div><div id="history-list"><p class="hint">Loading previous investigations…</p></div></section></aside></div>
|
| 6 |
<section id="results" hidden aria-labelledby="results-title"><div class="results-header"><div><div class="eyebrow" id="run-mode">INVESTIGATION</div><h2 id="results-title"></h2></div><div class="exports"><button id="export-md">Export notes ↓</button><button id="export-json">JSON ↓</button><button id="delete-run">Delete</button></div></div><div id="result-notice" class="notice"></div><nav class="tabs" aria-label="Research results"><button data-tab="findings" aria-current="page">Findings</button><button data-tab="evidence">Evidence library</button><button data-tab="agents">Agent notebook</button></nav><div id="findings" class="tab-panel"><div class="findings-grid"><section><h3>Knowledge gaps</h3><div id="gaps"></div></section><section><h3>Candidate hypotheses</h3><div id="hypotheses"></div></section></div><section class="limits"><h3>Limits of this investigation</h3><div id="limitations"></div></section></div><div id="evidence" class="tab-panel" hidden></div><div id="agents" class="tab-panel" hidden></div></section>
|
| 7 |
<footer><span>Pocket PhD</span><p>Research notebook</p></footer></main></body></html>
|
research.js
CHANGED
|
@@ -47,6 +47,7 @@ window.PocketResearch = (() => {
|
|
| 47 |
}
|
| 48 |
function outputSchema(shape, sourceIDs) {
|
| 49 |
if(typeof shape==='string') return {type:'string',maxLength:240,...(shape==='unverified'?{enum:['unverified']}:shape==='low|medium|high'?{enum:['low','medium','high']}:shape==='source ID'?{enum:[...sourceIDs]}:{})};
|
|
|
|
| 50 |
if(Array.isArray(shape)) return {type:'array',items:shape[0]==='source ID' && !sourceIDs.size?{type:'string'}:outputSchema(shape[0],sourceIDs),maxItems:shape[0]==='source ID'&&!sourceIDs.size?0:typeof shape[0]==='object'?2:6};
|
| 51 |
return {type:'object',properties:Object.fromEntries(Object.entries(shape).map(([key,value])=>[key,outputSchema(value,sourceIDs)])),required:Object.keys(shape),additionalProperties:false};
|
| 52 |
}
|
|
@@ -81,7 +82,7 @@ window.PocketResearch = (() => {
|
|
| 81 |
const {roles,schemas}=await response.json();
|
| 82 |
const prior={},agents=[];let failure=null;
|
| 83 |
// Bound context for the small local model; the notebook retains complete records.
|
| 84 |
-
const contextSources=evidence.sources.slice(0,6).map(source=>({...
|
| 85 |
const sourceIDs=new Set(contextSources.map(source=>source.id));
|
| 86 |
for(const [stage,name] of roles) {
|
| 87 |
if(failure) {agents.push({id:stage,name,status:'skipped',output:{}});continue;}
|
|
|
|
| 47 |
}
|
| 48 |
function outputSchema(shape, sourceIDs) {
|
| 49 |
if(typeof shape==='string') return {type:'string',maxLength:240,...(shape==='unverified'?{enum:['unverified']}:shape==='low|medium|high'?{enum:['low','medium','high']}:shape==='source ID'?{enum:[...sourceIDs]}:{})};
|
| 50 |
+
if(Array.isArray(shape) && shape[0]==='source ID' && !sourceIDs.size) return {type:'array',const:[]};
|
| 51 |
if(Array.isArray(shape)) return {type:'array',items:shape[0]==='source ID' && !sourceIDs.size?{type:'string'}:outputSchema(shape[0],sourceIDs),maxItems:shape[0]==='source ID'&&!sourceIDs.size?0:typeof shape[0]==='object'?2:6};
|
| 52 |
return {type:'object',properties:Object.fromEntries(Object.entries(shape).map(([key,value])=>[key,outputSchema(value,sourceIDs)])),required:Object.keys(shape),additionalProperties:false};
|
| 53 |
}
|
|
|
|
| 82 |
const {roles,schemas}=await response.json();
|
| 83 |
const prior={},agents=[];let failure=null;
|
| 84 |
// Bound context for the small local model; the notebook retains complete records.
|
| 85 |
+
const contextSources=evidence.sources.slice(0,6).map(source=>({id:source.id,title:source.title.slice(0,200),abstract:source.abstract.slice(0,800),year:Number.isInteger(source.year)?source.year:null}));
|
| 86 |
const sourceIDs=new Set(contextSources.map(source=>source.id));
|
| 87 |
for(const [stage,name] of roles) {
|
| 88 |
if(failure) {agents.push({id:stage,name,status:'skipped',output:{}});continue;}
|