Spaces:
Sleeping
Sleeping
File size: 10,014 Bytes
2eda359 | 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 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | import { useState } from 'react';
import { evalParsing } from '../api/client';
export default function ParsingEval({ file, accessToken, onClose }) {
const [loading, setLoading] = useState(false);
const [result, setResult] = useState(null);
const [error, setError] = useState(null);
const runEval = async () => {
setLoading(true);
setError(null);
setResult(null);
try {
const data = await evalParsing(file.path_lower, accessToken);
if (data.error) {
setError(data.error);
} else {
setResult(data);
}
} catch (err) {
setError(err.message);
}
setLoading(false);
};
// Format number with commas
const formatNumber = (num) => {
return num?.toLocaleString() || '0';
};
return (
<div className="fixed inset-0 bg-black/70 flex items-center justify-center z-50 p-4">
<div className="bg-slate-800 border border-slate-700 rounded-xl w-full max-w-2xl max-h-[85vh] flex flex-col shadow-xl">
{/* Header */}
<div className="p-4 border-b border-slate-700 flex items-center justify-between">
<div>
<h3 className="font-medium text-slate-100">Docling Parsing Evaluation</h3>
<p className="text-sm text-slate-400 mt-0.5 truncate max-w-md">{file.name}</p>
</div>
<button
type="button"
onClick={onClose}
className="p-1.5 hover:bg-slate-700 rounded-lg transition-colors"
aria-label="Close"
>
<svg className="w-5 h-5 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
{/* Content */}
<div className="flex-1 overflow-auto p-4">
{!result && !loading && !error && (
<div className="text-center py-8">
<svg className="w-16 h-16 text-slate-600 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4" />
</svg>
<p className="text-slate-300 mb-2">Test Docling's document parsing</p>
<p className="text-sm text-slate-500 mb-6">
This will download the file and analyze how Docling extracts structure
</p>
<button
type="button"
onClick={runEval}
className="px-6 py-2.5 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700 active:scale-[0.98] transition-all"
>
Run Parsing Evaluation
</button>
</div>
)}
{loading && (
<div className="text-center py-12">
<div className="w-10 h-10 border-3 border-blue-400 border-t-transparent rounded-full animate-spin mx-auto mb-4"></div>
<p className="text-slate-300">Parsing document with Docling...</p>
<p className="text-sm text-slate-500 mt-2">This may take a moment for large files</p>
</div>
)}
{error && (
<div className="bg-red-900/30 border border-red-700 rounded-lg p-4">
<div className="flex items-start gap-3">
<svg className="w-5 h-5 text-red-400 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<div>
<p className="text-red-400 font-medium">Parsing failed</p>
<p className="text-sm text-red-300 mt-1">{error}</p>
</div>
</div>
<button
type="button"
onClick={runEval}
className="mt-4 px-4 py-2 bg-slate-700 text-slate-200 rounded-lg text-sm hover:bg-slate-600 transition-colors"
>
Try Again
</button>
</div>
)}
{result && (
<div className="space-y-6">
{/* Status Badge */}
<div className="flex items-center gap-2">
{result.status === 'OK' ? (
<span className="flex items-center gap-1.5 bg-green-900/40 border border-green-700 text-green-400 px-3 py-1 rounded-full text-sm font-medium">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
Parsing Successful
</span>
) : (
<span className="flex items-center gap-1.5 bg-red-900/40 border border-red-700 text-red-400 px-3 py-1 rounded-full text-sm font-medium">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
{result.status}
</span>
)}
<span className="text-slate-500 text-sm">{result.format?.toUpperCase()}</span>
</div>
{/* Stats Grid */}
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
<div className="bg-slate-900 border border-slate-700 rounded-lg p-3 text-center">
<p className="text-2xl font-bold text-blue-400">{formatNumber(result.total_elements)}</p>
<p className="text-xs text-slate-500 mt-1">Elements</p>
</div>
<div className="bg-slate-900 border border-slate-700 rounded-lg p-3 text-center">
<p className="text-2xl font-bold text-green-400">{formatNumber(result.total_chars)}</p>
<p className="text-xs text-slate-500 mt-1">Characters</p>
</div>
<div className="bg-slate-900 border border-slate-700 rounded-lg p-3 text-center">
<p className="text-2xl font-bold text-purple-400">{formatNumber(result.total_words)}</p>
<p className="text-xs text-slate-500 mt-1">Words</p>
</div>
<div className="bg-slate-900 border border-slate-700 rounded-lg p-3 text-center">
<p className="text-2xl font-bold text-orange-400">{result.page_count || '-'}</p>
<p className="text-xs text-slate-500 mt-1">Pages</p>
</div>
</div>
{/* Element Types */}
{result.element_types && Object.keys(result.element_types).length > 0 && (
<div>
<h4 className="text-sm font-medium text-slate-300 mb-3">Element Types</h4>
<div className="bg-slate-900 border border-slate-700 rounded-lg divide-y divide-slate-700">
{Object.entries(result.element_types)
.sort((a, b) => b[1] - a[1])
.map(([type, count]) => (
<div key={type} className="flex items-center justify-between px-4 py-2.5">
<span className="text-slate-300 capitalize">{type.replace('_', ' ')}</span>
<span className="text-slate-400 font-mono">{count}</span>
</div>
))}
</div>
</div>
)}
{/* Sample Elements */}
{result.sample_elements && result.sample_elements.length > 0 && (
<div>
<h4 className="text-sm font-medium text-slate-300 mb-3">Sample Elements (first 10)</h4>
<div className="space-y-2">
{result.sample_elements.map((el, idx) => (
<div key={idx} className="bg-slate-900 border border-slate-700 rounded-lg p-3">
<div className="flex items-center gap-2 mb-2">
<span className={`text-xs font-medium px-2 py-0.5 rounded ${
el.type === 'heading' ? 'bg-blue-900/50 text-blue-300' :
el.type === 'table' ? 'bg-purple-900/50 text-purple-300' :
el.type === 'list_item' ? 'bg-orange-900/50 text-orange-300' :
'bg-slate-700 text-slate-300'
}`}>
{el.type}
</span>
{el.level && (
<span className="text-xs text-slate-500">Level {el.level}</span>
)}
</div>
<p className="text-sm text-slate-400 break-words">{el.text || '(empty)'}</p>
</div>
))}
</div>
</div>
)}
</div>
)}
</div>
{/* Footer */}
{result && (
<div className="p-4 border-t border-slate-700 flex justify-end gap-2">
<button
type="button"
onClick={runEval}
className="px-4 py-2 text-sm font-medium text-slate-300 hover:bg-slate-700 rounded-lg transition-colors"
>
Re-run
</button>
<button
type="button"
onClick={onClose}
className="px-4 py-2 text-sm font-medium bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
Done
</button>
</div>
)}
</div>
</div>
);
}
|