Spaces:
Build error
Build error
File size: 5,779 Bytes
e5726b7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | import { useState, useCallback, useRef } from 'react';
import { useDropzone } from 'react-dropzone';
import axios from 'axios';
import { Search, CheckCircle, AlertTriangle, Globe, ShieldCheck, FileText, Upload, X, Brain, Loader2 } from 'lucide-react';
import { motion, AnimatePresence } from 'framer-motion';
import AskExpertPanel from './AskExpertPanel';
const models = [
{ id: 'multi', label: 'Multi-Model Consensus (Safe Average)', provider: 'consensus' },
{ id: 'groq/openai/gpt-oss-120b', label: 'Groq OSS-120B (Powerhouse)', provider: 'groq' },
{ id: 'groq/llama-4-70b-versatile', label: 'Llama 4 70B (Fast Versatile)', provider: 'groq' },
{ id: 'kimi/kimi-3.0', label: 'Kimi 3.0 (NVIDIA Thinking)', provider: 'moonshot' },
{ id: 'qwen/qwen-max-2', label: 'Qwen Max 2 (Analytical Depth)', provider: 'dashscope' },
{ id: 'zai/glm-5', label: 'Z.ai GLM-5 (Reasoning Pro)', provider: 'zai' },
{ id: 'deepseek/r2', label: 'DeepSeek R2 (Logic & Strategy Beast)', provider: 'deepseek' },
{ id: 'openrouter/anthropic/claude-4-sonnet', label: 'Claude 4 Sonnet (via OpenRouter)', provider: 'openrouter' },
{ id: 'openrouter/openai/gpt-5o-mini', label: 'GPT-5o Mini (via OpenRouter)', provider: 'openrouter' },
{ id: 'openrouter/google/gemini-3-pro', label: 'Gemini 3 Pro (via OpenRouter)', provider: 'openrouter' },
{ id: 'gemini/gemini-2.5-pro-preview-06-05', label: 'Gemini 2.5 Pro (Direct)', provider: 'gemini' },
{ id: 'hf/NousResearch/Hermes-4-Llama-4-70B', label: 'Hermes-4 Llama 4 70B (HF)', provider: 'hf' },
];
export default function VerificationCore() {
const [query, setQuery] = useState('');
const [file, setFile] = useState(null);
const [filePreview, setFilePreview] = useState(null);
const [fileContent, setFileContent] = useState('');
const [results, setResults] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [selectedMode, setSelectedMode] = useState('multi');
const [bendMode, setBendMode] = useState(false);
const [searchStatus, setSearchStatus] = useState('');
const [progress, setProgress] = useState(0);
const abortControllerRef = useRef(null);
const onDrop = useCallback(async (acceptedFiles) => {
const uploadedFile = acceptedFiles[0];
if (!uploadedFile) return;
if (uploadedFile.size > 20 * 1024 * 1024) {
setError('File size exceeds 20MB limit');
return;
}
setFile(uploadedFile);
setError(null);
setFileContent('');
setFilePreview(null);
try {
if (uploadedFile.type.startsWith('image/')) {
const reader = new FileReader();
reader.onload = () => setFilePreview(reader.result);
reader.readAsDataURL(uploadedFile);
} else if (uploadedFile.type === 'application/pdf') {
const formData = new FormData();
formData.append('file', uploadedFile);
const response = await axios.post('/api/parse-pdf', formData);
setFileContent(response.data.text);
} else if (uploadedFile.type.startsWith('text/') || ['.md', '.csv', '.json'].some(ext => uploadedFile.name.toLowerCase().endsWith(ext))) {
const text = await uploadedFile.text();
setFileContent(text);
}
} catch (err) {
setError('Failed to process file: ' + err.message);
}
}, []);
const { getRootProps, getInputProps, isDragActive } = useDropzone({
onDrop,
accept: {
'image/*': ['.jpeg', '.jpg', '.png', '.webp', '.gif'],
'application/pdf': ['.pdf'],
'text/*': ['.txt', '.md', '.csv', '.json'],
},
maxFiles: 1,
maxSize: 20 * 1024 * 1024,
});
const removeFile = () => {
setFile(null);
setFilePreview(null);
setFileContent('');
setError(null);
};
const handleVerify = async (e) => {
e.preventDefault();
if (!query.trim() && !fileContent) {
setError('Please enter a query or upload a file');
return;
}
setLoading(true);
setError(null);
setResults([]);
setProgress(0);
abortControllerRef.current = new AbortController();
try {
let finalPrompt = query.trim();
let contextData = '';
if (fileContent) {
contextData = `\n\n[FILE CONTENT]:\n${fileContent.slice(0, 15000)}${fileContent.length > 15000 ? '... (truncated)' : ''}`;
finalPrompt += contextData;
}
if (bendMode && /current|latest|today|news|update|verify|fact-check/i.test(query)) {
setSearchStatus('Searching web for latest context...');
try {
const searchRes = await axios.post('/api/websearch', { query: query.slice(0, 100), maxResults: 5 }, { signal: abortControllerRef.current.signal });
const searchResults = searchRes.data.results || [];
if (searchResults.length > 0) {
const searchContext = searchResults.map((r, i) => `[${i + 1}] ${r.title}: ${r.snippet}`).join('\n');
finalPrompt += `\n\n[WEB SEARCH RESULTS]:\n${searchContext}`;
}
setSearchStatus('Web context integrated');
} catch (err) {
setSearchStatus('Web search failed, continuing without');
}
}
setProgress(30);
const progressInterval = setInterval(() => setProgress(p => Math.min(p + 10, 90)), 1000);
const response = await axios.post('/api/inference', {
prompt: finalPrompt,
model: selectedMode,
bendMode,
systemPrompt: bendMode
? 'You are in BEND MODE (hypothetical/strategic analysis). Prefix all outputs with [HYPOTHETICAL SCENARIO]. Provide analytical, strategic insights without real-world action.'
: 'You are a fact-checking and verification assistant. Provide accurate, sourced information with confidence scores.',
}, { signal: abortControllerRef |