feat: complete prediction UI feedback tasks (11.2, 11.4, 11.5)
- Implement disagreement visual highlighting with distinct colors - Yellow highlight for 'missed_by_human' predictions - Orange for 'label_mismatch' disagreements - Warning icon on disagreement markers - Add click-to-convert prediction feedback - Click disagreement predictions to create span annotations - Auto-fill with predicted label and times - Set source as 'model_confirmed' or 'model_corrected' - Add dismiss action for false positive predictions - Alt+Click or Ctrl+Click to dismiss predictions - Saves negative annotation with label 'O' - Records original prediction in model_prediction field - Filter predictions when 'Show only disagreements' is enabled
This commit is contained in:
parent
a18c6d110a
commit
65f00e6ce7
13 changed files with 905 additions and 11 deletions
|
|
@ -306,6 +306,83 @@ export default function Home() {
|
|||
setSelectedSpanId(spanId);
|
||||
};
|
||||
|
||||
// Handle prediction click to convert to annotation
|
||||
const handlePredictionClick = useCallback(async (span: PredictionSpan, disagreementType: string | null) => {
|
||||
if (!activeChartId) return;
|
||||
|
||||
// Find the span label type that matches the prediction label
|
||||
const matchingLabelType = spanLabelTypes.find((lt) => lt.name === span.label);
|
||||
|
||||
if (!matchingLabelType) {
|
||||
console.warn(`No span label type found for prediction label: ${span.label}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create span annotation from prediction
|
||||
try {
|
||||
const response = await fetch('/api/span-annotations', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
chart_id: activeChartId,
|
||||
start_time: span.start_time,
|
||||
end_time: span.end_time,
|
||||
label: span.label,
|
||||
confidence: 3, // Default confidence for model-confirmed annotations
|
||||
source: disagreementType === 'label_mismatch' ? 'model_corrected' : 'model_confirmed',
|
||||
model_prediction: {
|
||||
label: span.label,
|
||||
confidence: span.avg_confidence,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
await fetchSpanAnnotations(activeChartId);
|
||||
// Show a brief notification (you could add a toast notification here)
|
||||
console.log(`Created span annotation from prediction: ${span.label}`);
|
||||
} else {
|
||||
console.error('Failed to create span annotation from prediction');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error creating span annotation from prediction:', error);
|
||||
}
|
||||
}, [activeChartId, spanLabelTypes, fetchSpanAnnotations]);
|
||||
|
||||
// Handle prediction dismiss (save as negative annotation with label "O")
|
||||
const handlePredictionDismiss = useCallback(async (span: PredictionSpan, disagreementType: string | null) => {
|
||||
if (!activeChartId) return;
|
||||
|
||||
// Create negative annotation with label "O"
|
||||
try {
|
||||
const response = await fetch('/api/span-annotations', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
chart_id: activeChartId,
|
||||
start_time: span.start_time,
|
||||
end_time: span.end_time,
|
||||
label: 'O', // "O" means "not a pattern"
|
||||
confidence: 5, // High confidence for explicit user correction
|
||||
source: 'human_correction',
|
||||
model_prediction: {
|
||||
label: span.label,
|
||||
confidence: span.avg_confidence,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
await fetchSpanAnnotations(activeChartId);
|
||||
console.log(`Dismissed prediction as "not a pattern": ${span.label}`);
|
||||
} else {
|
||||
console.error('Failed to save negative annotation');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error saving negative annotation:', error);
|
||||
}
|
||||
}, [activeChartId, fetchSpanAnnotations]);
|
||||
|
||||
const handleDeleteSpan = async (spanId: number) => {
|
||||
try {
|
||||
const response = await fetch(`/api/span-annotations/${spanId}`, {
|
||||
|
|
@ -715,6 +792,8 @@ export default function Home() {
|
|||
modelInfo={predictionState.modelInfo}
|
||||
predictionSummary={predictionSummary}
|
||||
showOnlyDisagreements={showOnlyDisagreements}
|
||||
onPredictionClick={handlePredictionClick}
|
||||
onPredictionDismiss={handlePredictionDismiss}
|
||||
/>
|
||||
</main>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue