feat(iace): refresh architecture tab + data-flow diagram + E1 ingest script

- architecture.go: DataSources now reflect the real ingested set (ESAW 2023,
  BLS CFOI, OSHA OTM, PRISM, cobot CC-BY, HSE) with their RAG collections;
  risk stage cites BLS + the searchable RAG layer; matrix stage now mentions
  the distance-benchmark dimension.
- Architektur & Datenfluss tab: new DataFlowDiagram — 4 lanes (input →
  knowledge/RAG-evidence → deterministic engine → outputs) with live counts.
- scripts/ingest_iace_kb.sh: idempotent E1 ingest — creates the 2 collections
  and uploads the 6 datasources docs against a configurable RAG_URL (for prod
  Qdrant), with retry.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-06-12 09:18:03 +02:00
parent 99901bba0a
commit 755ea44343
4 changed files with 195 additions and 11 deletions
@@ -0,0 +1,98 @@
'use client'
import type { ReactNode } from 'react'
import type { Architecture } from '../_hooks/useArchitecture'
function Box({ title, sub, accent }: { title: string; sub?: string; accent?: 'purple' | 'amber' | 'green' | 'gray' }) {
const c =
accent === 'purple'
? 'border-purple-300 bg-purple-50/60 dark:border-purple-700 dark:bg-purple-900/20'
: accent === 'amber'
? 'border-amber-300 bg-amber-50/60 dark:border-amber-700 dark:bg-amber-900/20'
: accent === 'green'
? 'border-green-300 bg-green-50/60 dark:border-green-700 dark:bg-green-900/20'
: 'border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-800'
return (
<div className={`rounded-lg border ${c} px-2.5 py-1.5`}>
<div className="text-[11px] font-medium text-gray-800 dark:text-gray-200 leading-tight">{title}</div>
{sub && <div className="text-[10px] text-gray-500 leading-tight mt-0.5">{sub}</div>}
</div>
)
}
function Lane({ label, children }: { label: string; children: ReactNode }) {
return (
<div className="flex-1 min-w-[150px] space-y-2">
<div className="text-[10px] font-semibold uppercase tracking-wide text-gray-400 text-center">{label}</div>
<div className="space-y-1.5">{children}</div>
</div>
)
}
// Arrow between lanes: horizontal on desktop, down-chevron when wrapped.
function Arrow() {
return (
<div className="flex items-center justify-center text-gray-300 dark:text-gray-600 shrink-0 px-0.5">
<span className="hidden lg:block text-lg"></span>
<span className="lg:hidden text-sm"></span>
</div>
)
}
/**
* Audit data-flow diagram: where every datum enters, how it is processed and
* where it lands. Four lanes (input → knowledge/evidence → deterministic engine
* → outputs); counts are live from the architecture endpoint.
*/
export function DataFlowDiagram({ data }: { data: Architecture }) {
const libCount = (needle: string) => data.libraries.find((l) => l.name.includes(needle))?.count
const stages = data.stages
return (
<section className="space-y-2">
<h2 className="text-sm font-semibold text-gray-700 dark:text-gray-300">Datenfluss (Überblick)</h2>
<div className="rounded-xl border border-gray-200 dark:border-gray-700 bg-gray-50/50 dark:bg-gray-900/20 p-3 overflow-x-auto">
<div className="flex flex-col lg:flex-row gap-1.5 lg:items-stretch min-w-[280px]">
{/* 1 — Input */}
<Lane label="Eingabe">
<Box title="Grenzen-Formular" sub="17 Felder, EN ISO 12100" accent="purple" />
</Lane>
<Arrow />
{/* 2 — Knowledge + evidence */}
<Lane label="Wissensbasen + Evidenz">
<Box title="Code-Bibliotheken" sub={`Patterns ${libCount('Pattern') ?? ''} · Maßnahmen ${libCount('Maßnahmen') ?? ''} · Normen ${libCount('Normen') ?? ''} · OSHA-Abstände ${libCount('OSHA') ?? ''}`} />
<Box title="RAG bp_iace_accident_stats" sub="ESAW 2023 + BLS CFOI (Risiko-Anker)" accent="amber" />
<Box title="RAG bp_iace_safety_kb" sub="PRISM · Cobot · HSE · OSHA" accent="amber" />
</Lane>
<Arrow />
{/* 3 — Deterministic engine */}
<Lane label="Deterministische Engine">
<div className="rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 p-1.5 space-y-1">
{stages.map((s) => (
<div key={s.id} className="text-[10px] text-gray-600 dark:text-gray-300 leading-tight">
{s.title}
</div>
))}
</div>
</Lane>
<Arrow />
{/* 4 — Outputs */}
<Lane label="Ausgaben">
<Box title="Gefährdungen" sub="Szenario/Trigger/Harm/Zone" accent="green" />
<Box title="Maßnahmen" sub="+ OSHA-Mindestabstand" accent="green" />
<Box title="Risiko" sub="S/F/W/P + Konfidenz (Bereich)" accent="green" />
<Box title="Normen" sub="A/B/C, DIN↔OSHA" accent="green" />
<Box title="Benchmark" sub="Coverage + Abstands-Agreement %" accent="green" />
</Lane>
</div>
<p className="text-[10px] text-gray-400 mt-2">
Deterministische Engine (linksrechts) = reproduzierbar ohne LLM. Die RAG-Evidenz verankert/belegt die
Risiko-Zahlen, ersetzt aber nicht die Tier-Logik. Norm-Tabellen werden nie reproduziert.
</p>
</div>
</section>
)
}
@@ -2,6 +2,7 @@
import { useState } from 'react'
import { useArchitecture, type ArchStage } from './_hooks/useArchitecture'
import { DataFlowDiagram } from './_components/DataFlowDiagram'
export default function ArchitekturPage() {
const { data, loading } = useArchitecture()
@@ -26,6 +27,9 @@ export default function ArchitekturPage() {
</p>
</div>
{/* Data-flow overview diagram */}
<DataFlowDiagram data={data} />
{/* Pipeline flow */}
<section className="space-y-2">
<h2 className="text-sm font-semibold text-gray-700 dark:text-gray-300">Deterministische Pipeline</h2>