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