5a513181cc
Completes the advisor stack (FE + orchestration; /retrieve is SDK/RAG-owned). The route
now returns the FE contract instead of a text stream:
- retrieveFull() calls /retrieve with {query, context}; consumes clarity/evidence/
visual_evidence/footnotes (exact shape per board 2026-07-01 12:25).
- mode-routing (resolveMode): clarify unless a context was chosen and /retrieve's
clarity.mode says so. clarify -> L1 general answer (completeAdvisorAnswer, ungrounded,
no sources). answer -> L2 answer over numbered evidence with [n] markers.
- citations generated here ([n] -> nth evidence unit); footnotes remapped; evidence /
visual_evidence passed through.
- advisor-llm: non-streaming completeAdvisorAnswer(). Pure mappings in retrieve-mapping.ts
(+ tests). Removed the dead v2 evidence.ts/evidence-adapter (RegulationRef moved to
regulation-display). controls-augmentation kept (tested; re-integrable later).
NOT deployed: joint deploy with the SDK /retrieve endpoint (deploy-coupling). tsc clean,
25 vitest (mapping/clarify/answer/markdown/registry/rag), check-loc 0.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
58 lines
2.3 KiB
TypeScript
58 lines
2.3 KiB
TypeScript
// Human-readable display for regulations. Maps messy codes/short-names to a stable FAMILY key +
|
||
// friendly label (+ chapter for multi-part works like the DSK SDM). Presentation layer only:
|
||
// it bridges G2 (clean RAG metadata) and keeps working once codes are clean. Extend the table freely.
|
||
|
||
export interface RegulationRef {
|
||
code?: string
|
||
name?: string
|
||
short?: string
|
||
}
|
||
|
||
export interface RegulationDisplay {
|
||
familyKey: string // stable key used to GROUP evidence
|
||
familyLabel: string // human-readable regulation name
|
||
chapter?: string // e.g. "B51" for a DSK SDM building block
|
||
}
|
||
|
||
interface Rule {
|
||
test: RegExp
|
||
key: string
|
||
label: string
|
||
chapter?: RegExp
|
||
}
|
||
|
||
// Order matters: more specific patterns first.
|
||
const RULES: Rule[] = [
|
||
{
|
||
test: /dsk.?sdm|standard.?datenschutzmodell|(^|[^a-z])sdm([^a-z]|$)/i,
|
||
key: 'dsk_sdm',
|
||
label: 'DSK Standard-Datenschutzmodell (SDM)',
|
||
chapter: /\b([A-Z]\d{1,3})\b/,
|
||
},
|
||
{ test: /cyber.?resilience|(^|[^a-z])cra([^a-z]|$)/i, key: 'cra', label: 'Cyber Resilience Act (CRA)' },
|
||
{ test: /(^|[^a-z])nis.?2([^a-z]|$)/i, key: 'nis2', label: 'NIS2-Richtlinie' },
|
||
{ test: /data.?privacy.?framework|(^|[^a-z])dpf([^a-z]|$)/i, key: 'dpf', label: 'EU-US Data Privacy Framework' },
|
||
{ test: /maschinen|2023.?1230/i, key: 'maschinenvo', label: 'Maschinenverordnung (EU) 2023/1230' },
|
||
{ test: /ds.?gvo|gdpr/i, key: 'dsgvo', label: 'DSGVO – Datenschutz-Grundverordnung' },
|
||
{ test: /(^|[^a-z])bdsg([^a-z]|$)/i, key: 'bdsg', label: 'BDSG – Bundesdatenschutzgesetz' },
|
||
{ test: /tdddg|ttdsg/i, key: 'tddg', label: 'TDDDG (Digitale-Dienste-Datenschutz)' },
|
||
{ test: /edpb|edsa|(^|[^a-z])wp\s?\d+/i, key: 'edpb', label: 'EDPB / DSK Leitlinien' },
|
||
{ test: /(^|[^a-z])bsi([^a-z]|$)/i, key: 'bsi', label: 'BSI' },
|
||
]
|
||
|
||
export function resolveRegulation(reg: RegulationRef): RegulationDisplay {
|
||
const hay = `${reg.code || ''} ${reg.short || ''} ${reg.name || ''}`
|
||
for (const r of RULES) {
|
||
if (r.test.test(hay)) {
|
||
const chapter = r.chapter
|
||
? r.chapter.exec(reg.short || reg.code || '')?.[1] || undefined
|
||
: undefined
|
||
return { familyKey: r.key, familyLabel: r.label, chapter }
|
||
}
|
||
}
|
||
return {
|
||
familyKey: reg.code || reg.short || 'unknown',
|
||
familyLabel: reg.short || reg.name || reg.code || 'Regelwerk',
|
||
}
|
||
}
|