import React, { useState } from "react"; import { apiClient, ExplanationResult, SpectrumData } from "../apiClient"; import "../static/style.css"; interface ExplainabilityPanelProps { spectrumData: SpectrumData | null; selectedModel: string; modality: "raman" | "ftir"; onExplainabilityResult: (result: ExplanationResult) => void; } const ExplainabilityPanel: React.FC = ({ spectrumData, selectedModel, modality, onExplainabilityResult, }) => { const [isLoading, setIsLoading] = useState(false); const [explanation, setExplanation] = useState( null ); const [error, setError] = useState(null); const analyzeWithExplanation = async () => { if (!spectrumData) { setError("No spectrum data is available for analysis."); return; } setIsLoading(true); setError(null); try { const result = await apiClient.explainSpectrum({ spectrum: spectrumData, model_name: selectedModel, modality: modality, include_provenance: true, // Added the missing property }); setExplanation(result); onExplainabilityResult(result); } catch (err) { setError( err instanceof Error ? err.message : "An unknown error occurred during analysis." ); } finally { setIsLoading(false); } }; const renderFeatureImportance = () => { if (!explanation?.feature_importance) return null; const { feature_importance, prediction, class_labels } = explanation; const { method = "", summary = { max_importance: 0, mean_importance: 0, important_region_start: 0, important_region_end: 0, }, top_features = { indices: [], values: [] }, } = feature_importance; // Combine indices and values, sort by importance, and take the top 10 for safe rendering const topFeaturesData = top_features.indices .map((index: number, i: number) => ({ index, value: top_features.values[i], })) .sort((a, b) => b.value - a.value) .slice(0, 10); const maxImportance = summary.max_importance > 0 ? summary.max_importance : 1; return (
{method.replace(/_/g, " ")}
{summary.max_importance?.toFixed(4) ?? "N/A"}
{summary.mean_importance?.toFixed(4) ?? "N/A"}
{summary.important_region_start} - {summary.important_region_end}

Top 10 Most Important Features

{topFeaturesData.map(({ index, value }) => (
#{index}
{value.toFixed(3)}
))}

Interpretation Guide

  • Feature Index: The position (wavenumber) in the processed spectrum.
  • Importance Score: How much a feature contributed to the final prediction.
  • High scores indicate features that strongly influenced the model's decision towards **{class_labels[prediction]}**.
); }; const renderPredictionSummary = () => { if (!explanation) return null; const { prediction, confidence, probabilities, class_labels, model_used, spectrum_filename, } = explanation; const predictedClass = class_labels[prediction].toLowerCase(); const confidencePercent = (confidence * 100).toFixed(1); return (
{predictedClass.toUpperCase()}
{confidencePercent}% Confidence
{Object.entries(class_labels).map(([index, label]) => { const numericIndex = Number(index); return (
{label}
{(probabilities[numericIndex] * 100).toFixed(1)}%
); })}

Model: {model_used} | File: {spectrum_filename || "N/A"}

); }; return (

AI Explainability

Understand the "why" behind the model's prediction by identifying which spectral features were most influential.

{error &&
{error}
} {explanation ? (

Prediction Summary

{renderPredictionSummary()}

Feature Importance

{renderFeatureImportance()}
) : ( !isLoading && !error && (

{spectrumData ? "Click 'Explain Prediction' to begin analysis." : "Upload a spectrum on the 'Standard Analysis' tab to enable explainability."}

) )}
); }; export default ExplainabilityPanel;