User:Polygnotus/Scripts/AI Source Verification.js: Difference between revisions

Content deleted Content added
Created page with '(function() { 'use strict'; class WikipediaSourceVerifier { constructor() { this.providers = { claude: { name: 'Claude', storageKey: 'claude_api_key', color: '#0645ad', model: 'claude-sonnet-4-20250514' }, gemini: { name: 'Gemini', storageKey: 'gemini_api_key'...'
 
No edit summary
 
(5 intermediate revisions by the same user not shown)
Line 1:
//Inspired by User:Phlsph7/SourceVerificationAIAssistant.js
 
(function() {
'use strict';
Line 200 ⟶ 202:
font-size: 13px;
line-height: 1.5;
max-height: 200px480px;
overflow-y: auto;
white-space: pre-wrap;
Line 356 ⟶ 358:
if (this.getCurrentApiKey()) {
// Enable verify button if we have API key and either claim+source OR just show it enabled
this.buttons.verify.setDisabled(!this.activeClaim || !this.activeSource);
const hasClaimAndSource = this.activeClaim && this.activeSource;
this.buttons.verify.setDisabled(!hasClaimAndSource);
container.appendChild(this.buttons.verify.$element[0]);
container.appendChild(this.buttons.changeKey.$element[0]);
container.appendChild(this.buttons.removeKey.$element[0]);
// Debug logging
console.log('Button visibility update:', {
hasApiKey: !!this.getCurrentApiKey(),
activeClaim: !!this.activeClaim,
activeSource: !!this.activeSource,
buttonDisabled: !hasClaimAndSource
});
} else {
this.buttons.verify.setDisabled(true);
Line 431 ⟶ 443:
document.getElementById('verifier-claim-text').textContent = claim;
// Fetch source content (URL extraction)
this.updateStatus('FetchingExtracting source contentURL...');
const sourceContentsourceInfo = await this.fetchSourceContent(refUrl);
if (!sourceContentsourceInfo) {
this.updateStatus('Could not fetchextract source contentURL', true);
return;
}
// Update UI with source info
this.activeSource = sourceContentsourceInfo;
const sourceElement = document.getElementById('verifier-source-text');
sourceElement.textContent = sourceContent.substring(0, 1000) + (sourceContent.length > 1000 ? '...' : '');
// EnableShow verifythe buttonURL and indicate content will be fetched by AI
const urlMatch = sourceInfo.match(/Source URL: (https?:\/\/[^\s\n]+)/);
if (urlMatch) {
sourceElement.innerHTML = `<strong>Source URL:</strong><br><a href="${urlMatch[1]}" target="_blank" style="word-break: break-all;">${urlMatch[1]}</a><br><br><em>Content will be fetched and analyzed by AI during verification.</em>`;
} else {
sourceElement.textContent = sourceInfo;
}
// Enable verify button now that we have both claim and source
this.updateButtonVisibility();
this.updateStatus('Ready to verify claim against source');
console.log('Reference click completed:', {
claim: this.activeClaim ? 'Set' : 'Missing',
source: this.activeSource ? 'Set' : 'Missing',
apiKey: this.getCurrentApiKey() ? 'Set' : 'Missing'
});
} catch (error) {
Line 455 ⟶ 480:
}
 
extractClaimText(refElement) {
 
// Find the text node containing the reference
 
let currentNode = refElement.parentElement.previousSibling;
extractClaimText(refElement) {
let claim = '';
// Get the paragraph or container element
const container = refElement.closest('p, li, td, div, section');
// Look backwards to find the start of the sentence
whileif (currentNode!container) {
return '';
if (currentNode.nodeType === Node.TEXT_NODE) {
}
const text = currentNode.textContent;
claim = text + claim;
// Get all text content from the container
let fullText = '';
// Stop at sentence boundaries
const walker = document.createTreeWalker(
const sentenceEnd = text.match(/[.!?]\s*$/);
container,
if (sentenceEnd) {
break;NodeFilter.SHOW_TEXT,
}null,
false
} else if (currentNode.nodeType === Node.ELEMENT_NODE) {
);
// Include text from elements but stop at certain boundaries
if (currentNode.tagName && ['BR', 'P', 'DIV', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6'].includes(currentNode.tagName)) {
let textNode;
break;
const textNodes = [];
}
while (textNode = walker.nextNode()) {
claim = currentNode.textContent + claim;
}textNodes.push({
node: textNode,
currentNode = currentNode.previousSibling;
}text: textNode.textContent,
offset: fullText.length
});
// Clean up the claim text
fullText claim += claimtextNode.trim()textContent;
}
// If we didn't find a sentence boundary, try to extract from current element
// Find the position of the reference in the full text
if (!claim || claim.length < 10) {
const parentElementrefText = refElement.closest('p,textContent; li,// td,This div');should be like "[1]"
let refPosition = -1;
if (parentElement) {
const fullText = parentElement.textContent;
// Look for the reference pattern in the text
const refIndex = fullText.indexOf('[');
const refPattern = new RegExp('\\[\\s*' + refText.replace(/[\[\]]/g, '') + '\\s*\\]');
if (refIndex > 0) {
const claimrefMatch = fullText.substring(0, refIndex).trimmatch(refPattern);
if (refMatch) {
// Find the last sentence before the reference
refPosition = refMatch.index;
const sentences = claim.split(/[.!?]/);
} else {
if (sentences.length > 1) {
// Fallback: find any reference pattern before our element
claim = sentences[sentences.length - 2] + '.';
const allRefs = }fullText.match(/\[\d+\]/g);
if (allRefs) }{
// Find the last }reference before the end
}let lastRefIndex = -1;
let searchPos = 0;
returnfor claim.trim(const ref of allRefs); {
const index = fullText.indexOf(ref, searchPos);
}
if (index !== -1) {
lastRefIndex = index;
searchPos = index + ref.length;
}
}
refPosition = lastRefIndex;
}
}
if (refPosition === -1) {
refPosition = fullText.length;
}
// Extract text before the reference
const textBeforeRef = fullText.substring(0, refPosition).trim();
// Find the last complete sentence
const sentences = textBeforeRef.split(/([.!?]+\s+)/);
if (sentences.length === 1) {
// No sentence breaks found, return the entire text
return textBeforeRef;
}
// Reconstruct the last sentence
let lastSentence = '';
let foundSentenceEnd = false;
// Work backwards through the sentence fragments
for (let i = sentences.length - 1; i >= 0; i--) {
const fragment = sentences[i];
if (fragment.match(/^[.!?]+\s*$/)) {
// This is punctuation + whitespace
if (!foundSentenceEnd) {
foundSentenceEnd = true;
continue;
} else {
// We've found the end of the previous sentence
break;
}
} else {
// This is text content
lastSentence = fragment + lastSentence;
if (foundSentenceEnd) {
// We have a complete sentence
break;
}
}
}
// If we didn't find a proper sentence boundary, fall back to a more aggressive approach
if (!lastSentence.trim() || lastSentence.trim().length < 10) {
// Look for other potential sentence boundaries
const alternativeBreaks = textBeforeRef.split(/([.!?:;]\s+|^\s*[A-Z])/);
if (alternativeBreaks.length > 1) {
lastSentence = alternativeBreaks[alternativeBreaks.length - 1];
} else {
// As a last resort, take a reasonable chunk from the end
const words = textBeforeRef.split(/\s+/);
if (words.length > 15) {
lastSentence = words.slice(-15).join(' ');
} else {
lastSentence = textBeforeRef;
}
}
}
// Clean up the result
lastSentence = lastSentence.trim();
// Ensure it ends with proper punctuation for context
if (lastSentence && !lastSentence.match(/[.!?]$/)) {
// Check if the original text continues with punctuation
const nextChar = fullText.charAt(refPosition - lastSentence.length - 1);
if (nextChar.match(/[.!?]/)) {
lastSentence = nextChar + ' ' + lastSentence;
}
}
// Remove any remaining reference brackets that might have been included
lastSentence = lastSentence.replace(/\[\d+\]/g, '').trim();
return lastSentence;
}
 
 
 
 
 
 
 
extractReferenceUrl(refElement) {
Line 529 ⟶ 646:
async fetchSourceContent(url) {
// Instead of trying to fetch the content directly (which fails due to CORS),
// we'll use the AI API to fetch and analyze the content
try {
// UseFor CORSnow, proxyreturn forthe externalURL URLs- the AI will fetch it during verification
return `Source URL: ${url}\n\n[Content will be fetched by AI during verification]`;
const proxyUrl = `https://corsproxy.io/?${encodeURIComponent(url)}`;
const response = await fetch(proxyUrl, {
headers: {
'User-Agent': 'Mozilla/5.0 (compatible; WikipediaSourceVerifier/1.0)'
}
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const html = await response.text();
// Extract text content from HTML
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
// Remove script and style elements
const unwanted = doc.querySelectorAll('script, style, nav, header, footer, aside, .ad, .advertisement');
unwanted.forEach(el => el.remove());
// Try to find main content
let mainContent = doc.querySelector('main, article, .content, .post-content, .article-content, .story-body')
|| doc.querySelector('body');
if (!mainContent) {
mainContent = doc.body;
}
let text = mainContent.textContent || mainContent.innerText || '';
// Clean up the text
text = text.replace(/\s+/g, ' ').trim();
// Limit length to avoid token limits
if (text.length > 10000) {
text = text.substring(0, 10000);
}
return text;
} catch (error) {
console.error('Error fetchingwith source contentURL:', error);
throw new Error(`FailedInvalid tosource fetch sourceURL: ${error.message}`);
}
}
Line 742 ⟶ 820:
this.updateButtonVisibility();
this.updateStatus('API key set successfully!');
// If we already have claim and source, enable verify button
if (this.activeClaim && this.activeSource) {
console.log('API key set - enabling verification');
this.updateButtonVisibility();
}
}
}
Line 804 ⟶ 888:
}
async callClaudeAPI(claim, sourcesourceInfo) {
// Extract URL from sourceInfo
const urlMatch = sourceInfo.match(/Source URL: (https?:\/\/[^\s\n]+)/);
const sourceUrl = urlMatch ? urlMatch[1] : null;
const requestBody = {
model: this.providers.claude.model,
max_tokens: 20003000,
system: `You are a fact-checking assistant. Your task is to verify whether a claim from a Wikipedia article is supported by the provided source content at the provided URL.
 
Instructions:
1. First, fetch and read the content from the provided URL
1. Analyze the claim and determine what specific facts it asserts
2. Analyze the claim and determine what specific facts it asserts
2. Search through the source content for information that supports or contradicts the claim
3. Search through the source content for information that supports or contradicts the claim
3. Provide a clear verdict: SUPPORTED, PARTIALLY SUPPORTED, NOT SUPPORTED, or UNCLEAR
4. Provide a clear verdict: SUPPORTED, PARTIALLY SUPPORTED, NOT SUPPORTED, or UNCLEAR
4. Quote the specific sentences from the source that support your verdict
5. Quote the specific sentences from the source that support your verdict
5. Explain any discrepancies or missing information
6. Explain any discrepancies or missing information
 
Be precise and objective in your analysis.`,
messages: [{
role: "user",
content: `ClaimsourceUrl to? verify: "${claim}"
`Please fetch the content from this URL and verify the claim against it.
 
SourceClaim contentto verify: "${sourceclaim}"`
 
Source URL: ${sourceUrl}` :
`Claim to verify: "${claim}"
 
Source content: "${sourceInfo}"`
}]
};
Line 846 ⟶ 941:
}
async callGeminiAPI(claim, sourcesourceInfo) {
const API_URL = `https://generativelanguage.googleapis.com/v1beta/models/${this.providers.gemini.model}:generateContent?key=${this.getCurrentApiKey()}`;
// Extract URL from sourceInfo
const systemPrompt = `You are a fact-checking assistant. Verify whether a Wikipedia claim is supported by the provided source content.
const urlMatch = sourceInfo.match(/Source URL: (https?:\/\/[^\s\n]+)/);
const sourceUrl = urlMatch ? urlMatch[1] : null;
const systemPrompt = `You are a fact-checking assistant. Verify whether a Wikipedia claim is supported by the source content at the provided URL.
 
Instructions:
Provide:
1. Fetch and read the content from the provided URL
1. A clear verdict: SUPPORTED, PARTIALLY SUPPORTED, NOT SUPPORTED, or UNCLEAR
2. Analyze the claim and determine what specific facts it asserts
2. Specific quotes from the source that support your verdict
3. Search through the source content for information that supports or contradicts the claim
3. Explanation of any discrepancies or missing information
4. Provide a clear verdict: SUPPORTED, PARTIALLY SUPPORTED, NOT SUPPORTED, or UNCLEAR
5. Quote the specific sentences from the source that support your verdict
6. Explain any discrepancies or missing information
 
Be precise and objective in your analysis.`;
Line 860 ⟶ 962:
const requestBody = {
contents: [{
parts: [{ "text": `ClaimsourceUrl to? verify: "${claim}"\n\nSource content: "${source}"` }],
`Please fetch the content from this URL and verify the claim against it.
 
Claim to verify: "${claim}"
 
Source URL: ${sourceUrl}` :
`Claim to verify: "${claim}"
 
Source content: "${sourceInfo}"` }],
}],
systemInstruction: {
Line 869 ⟶ 979:
temperature: 0.0,
},
tools: [
{urlContext: {}},
{googleSearch: {}},
],
};
Line 895 ⟶ 1,009:
}
async callOpenAIAPI(claim, sourcesourceInfo) {
// Extract URL from sourceInfo
const urlMatch = sourceInfo.match(/Source URL: (https?:\/\/[^\s\n]+)/);
const sourceUrl = urlMatch ? urlMatch[1] : null;
const requestBody = {
model: this.providers.openai.model,
Line 902 ⟶ 1,020:
{
role: "system",
content: `You are a fact-checking assistant. Your task is to verify whether a claim from a Wikipedia article is supported by the provided source content.
 
Instructions:
1. Analyze the claim and determine what specific facts it asserts
2. Search throughExamine the source content for information that supports or contradicts the claimprovided
3. Provide a clear verdict: SUPPORTED, PARTIALLY SUPPORTED, NOT SUPPORTED, or UNCLEAR
4. Explain your reasoning based on the available information
4. Quote the specific sentences from the source that support your verdict
5. Note any limitations due to inability to access the full source content
5. Explain any discrepancies or missing information
 
Be precise and objective in your analysis.`
Line 915 ⟶ 1,033:
{
role: "user",
content: `ClaimsourceUrl to? verify: "${claim}"\n\nSource content: "${source}"`
`I need to verify this claim against a source, but I can only provide the URL since direct content fetching isn't available.
 
Claim to verify: "${claim}"
 
Source URL: ${sourceUrl}
 
Please provide analysis based on what you can determine from the URL and any known information about the source. Note that full verification would require accessing the complete source content.` :
`Claim to verify: "${claim}"
 
Source information: "${sourceInfo}"`
}
],