From 6940271672d689745d83a70ba8fd31243a44a343 Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Wed, 13 May 2026 15:36:18 +0200 Subject: [PATCH] feat(iace): expandable detail comparison in benchmark tab Backend: HazardSummary now includes description, scenario, possible_harm, trigger_event, and mitigations[] for side-by-side comparison. Frontend: Each matched pair row is now clickable/expandable showing two-column detail view: - Left (GT): hazard type, cause, zone, lifecycle phases, risk values (F/W/P/S->R), residual risk, measures, type (KM/TM/BI), norms, comment - Right (Engine): name, scenario, zone, possible harm, trigger, measures Co-Authored-By: Claude Opus 4.6 (1M context) --- .../_components/HazardComparisonTable.tsx | 118 ++++++++++++++---- .../benchmark/_hooks/useBenchmark.ts | 3 + .../internal/iace/benchmark_matcher.go | 19 ++- .../internal/iace/benchmark_types.go | 19 +-- 4 files changed, 121 insertions(+), 38 deletions(-) diff --git a/admin-compliance/app/sdk/iace/[projectId]/benchmark/_components/HazardComparisonTable.tsx b/admin-compliance/app/sdk/iace/[projectId]/benchmark/_components/HazardComparisonTable.tsx index dfa8417..be4afbb 100644 --- a/admin-compliance/app/sdk/iace/[projectId]/benchmark/_components/HazardComparisonTable.tsx +++ b/admin-compliance/app/sdk/iace/[projectId]/benchmark/_components/HazardComparisonTable.tsx @@ -53,6 +53,7 @@ export function HazardComparisonTable({ matched, missing, extra }: Props) { } function MatchedTable({ pairs }: { pairs: HazardMatchPair[] }) { + const [expanded, setExpanded] = useState>({}) if (pairs.length === 0) return return ( @@ -60,10 +61,10 @@ function MatchedTable({ pairs }: { pairs: HazardMatchPair[] }) { - + - + @@ -71,32 +72,41 @@ function MatchedTable({ pairs }: { pairs: HazardMatchPair[] }) { const quality = p.match_score >= 0.7 ? 'green' : p.match_score >= 0.4 ? 'yellow' : 'red' const rowBg = quality === 'green' ? 'bg-green-50/30 dark:bg-green-900/5' : quality === 'yellow' ? 'bg-yellow-50/30 dark:bg-yellow-900/5' : '' - const gtRiskLevel = p.gt_entry.risk_in.r >= 30 ? 'hoch' : p.gt_entry.risk_in.r >= 15 ? 'mittel' : 'niedrig' + const isOpen = expanded[i] return ( - - - - - - - - + + setExpanded(prev => ({ ...prev, [i]: !prev[i] }))}> + + + + + + + + {isOpen && ( + + + + )} + ) })} @@ -104,6 +114,60 @@ function MatchedTable({ pairs }: { pairs: HazardMatchPair[] }) { ) } +/** Side-by-side detail comparison of GT entry vs. Engine hazard */ +function DetailComparison({ gt, engine }: { gt: GroundTruthEntry; engine: HazardSummary }) { + return ( +
+ {/* Left: Ground Truth */} +
+
Ground Truth (Fachmann)
+ + + + + R=${gt.risk_in.r}`} /> + {gt.risk_out.r > 0 && ( + R=${gt.risk_out.r}`} /> + )} + + + {gt.norm_references?.length > 0 && ( + + )} + + {gt.comment && } +
+ {/* Right: Engine */} +
+
Engine (automatisch)
+ + + + + + {engine.mitigations && engine.mitigations.length > 0 ? ( + + ) : ( + + )} +
+
+ ) +} + +function DetailRow({ label, gt, multiline }: { label: string; gt: string; multiline?: boolean }) { + return ( +
+
{label}
+ {multiline ? ( +
{gt}
+ ) : ( +
{gt}
+ )} +
+ ) +} + function MissingTable({ entries }: { entries: GroundTruthEntry[] }) { if (entries.length === 0) return return ( diff --git a/admin-compliance/app/sdk/iace/[projectId]/benchmark/_hooks/useBenchmark.ts b/admin-compliance/app/sdk/iace/[projectId]/benchmark/_hooks/useBenchmark.ts index f309cb8..74bad25 100644 --- a/admin-compliance/app/sdk/iace/[projectId]/benchmark/_hooks/useBenchmark.ts +++ b/admin-compliance/app/sdk/iace/[projectId]/benchmark/_hooks/useBenchmark.ts @@ -31,6 +31,9 @@ export interface GroundTruthEntry { export interface HazardSummary { id: string; name: string; category: string component?: string; zone?: string; risk_level?: string + description?: string; scenario?: string + possible_harm?: string; trigger_event?: string + mitigations?: string[] } export interface HazardMatchPair { diff --git a/ai-compliance-sdk/internal/iace/benchmark_matcher.go b/ai-compliance-sdk/internal/iace/benchmark_matcher.go index 685264d..663ea69 100644 --- a/ai-compliance-sdk/internal/iace/benchmark_matcher.go +++ b/ai-compliance-sdk/internal/iace/benchmark_matcher.go @@ -59,13 +59,24 @@ func CompareBenchmark(gt *GroundTruth, hazards []Hazard, mitigations []Mitigatio return &BenchmarkResult{} } + // Build mitigation names per hazard + mitNamesByHazard := make(map[string][]string) + for _, m := range mitigations { + mitNamesByHazard[m.HazardID.String()] = append(mitNamesByHazard[m.HazardID.String()], m.Name) + } + engineSummaries := make([]HazardSummary, len(hazards)) for i, h := range hazards { engineSummaries[i] = HazardSummary{ - ID: h.ID.String(), - Name: h.Name, - Category: h.Category, - Zone: h.HazardousZone, + ID: h.ID.String(), + Name: h.Name, + Category: h.Category, + Zone: h.HazardousZone, + Description: h.Description, + Scenario: h.Scenario, + PossibleHarm: h.PossibleHarm, + TriggerEvent: h.TriggerEvent, + Mitigations: mitNamesByHazard[h.ID.String()], } } diff --git a/ai-compliance-sdk/internal/iace/benchmark_types.go b/ai-compliance-sdk/internal/iace/benchmark_types.go index f98b976..b3d5646 100644 --- a/ai-compliance-sdk/internal/iace/benchmark_types.go +++ b/ai-compliance-sdk/internal/iace/benchmark_types.go @@ -90,14 +90,19 @@ type HazardMatchPair struct { MatchReason string `json:"match_reason"` } -// HazardSummary is a lightweight hazard representation for benchmark results. +// HazardSummary is a hazard representation for benchmark results with detail fields. type HazardSummary struct { - ID string `json:"id"` - Name string `json:"name"` - Category string `json:"category"` - Component string `json:"component,omitempty"` - Zone string `json:"zone,omitempty"` - RiskLevel string `json:"risk_level,omitempty"` + ID string `json:"id"` + Name string `json:"name"` + Category string `json:"category"` + Component string `json:"component,omitempty"` + Zone string `json:"zone,omitempty"` + RiskLevel string `json:"risk_level,omitempty"` + Description string `json:"description,omitempty"` + Scenario string `json:"scenario,omitempty"` + PossibleHarm string `json:"possible_harm,omitempty"` + TriggerEvent string `json:"trigger_event,omitempty"` + Mitigations []string `json:"mitigations,omitempty"` } // CategoryScore shows coverage per ISO 12100 hazard group.
Nr. Ground TruthR(GT)R Engine ScoreMatchQualitaet
{p.gt_entry.nr} -
{p.gt_entry.hazard_type}
-
{p.gt_entry.component_zone}
-
- -
- F{p.gt_entry.risk_in.f} W{p.gt_entry.risk_in.w} P{p.gt_entry.risk_in.p} S{p.gt_entry.risk_in.s} -
-
-
{p.engine_hazard.name}
-
{p.engine_hazard.category}
-
- - - -
{p.match_reason}
-
+
+ + + + {p.gt_entry.nr} +
+
+
{p.gt_entry.hazard_type}
+
{p.gt_entry.component_zone}
+
+ + +
{p.engine_hazard.name}
+
{p.engine_hazard.category}
+
+ +