Files
breakpilot-compliance/admin-compliance/app/api/sdk/drafting-engine/draft/draft-helpers.ts
Sharang Parnerkar feedeb052f refactor(admin-compliance): split 11 oversized files under 500 LOC hard cap (batch 2)
Barrel-split pattern: each original becomes a thin re-export barrel; logic
moved to sibling files so no consumer imports need updating.

Files split:
- loeschfristen-profiling.ts → profiling-data.ts + profiling-generator.ts
- vendor-compliance/catalog/vendor-templates.ts → vendor-country-profiles.ts
- vendor-compliance/catalog/legal-basis.ts → legal-basis-retention.ts
- dsfa/eu-legal-frameworks.ts → eu-legal-frameworks-national.ts
- compliance-scope-types/document-scope-matrix-core.ts → core-part2.ts
- compliance-scope-types/document-scope-matrix-extended.ts → extended-part2.ts
- app/sdk/document-generator/contextBridge.ts → contextBridge-helpers.ts
- app/api/sdk/drafting-engine/draft/route.ts → draft-helpers.ts + draft-helpers-v2.ts

All files ≤ 500 LOC. Zero behavior changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 00:32:08 +02:00

162 lines
5.8 KiB
TypeScript

/**
* Drafting Engine - Draft Helper Functions (v1 pipeline + shared constants)
*
* Shared state, v1 legacy pipeline helpers.
* v2 pipeline lives in draft-helpers-v2.ts.
*/
import { NextResponse } from 'next/server'
import { buildVVTDraftPrompt } from '@/lib/sdk/drafting-engine/prompts/draft-vvt'
import { buildTOMDraftPrompt } from '@/lib/sdk/drafting-engine/prompts/draft-tom'
import { buildDSFADraftPrompt } from '@/lib/sdk/drafting-engine/prompts/draft-dsfa'
import { buildPrivacyPolicyDraftPrompt } from '@/lib/sdk/drafting-engine/prompts/draft-privacy-policy'
import { buildLoeschfristenDraftPrompt } from '@/lib/sdk/drafting-engine/prompts/draft-loeschfristen'
import type { DraftContext, DraftResponse, DraftRevision, DraftSection } from '@/lib/sdk/drafting-engine/types'
import type { ScopeDocumentType } from '@/lib/sdk/compliance-scope-types'
import { ConstraintEnforcer } from '@/lib/sdk/drafting-engine/constraint-enforcer'
import { ProseCacheManager } from '@/lib/sdk/drafting-engine/cache'
import { queryRAG } from '@/lib/sdk/drafting-engine/rag-query'
import { DOCUMENT_RAG_CONFIG } from '@/lib/sdk/drafting-engine/rag-config'
const OLLAMA_URL = process.env.OLLAMA_URL || 'http://host.docker.internal:11434'
const LLM_MODEL = process.env.COMPLIANCE_LLM_MODEL || 'qwen2.5vl:32b'
export const constraintEnforcer = new ConstraintEnforcer()
export const proseCache = new ProseCacheManager({ maxEntries: 200, ttlHours: 24 })
export const TEMPLATE_VERSION = '2.0.0'
export const TERMINOLOGY_VERSION = '1.0.0'
export const VALIDATOR_VERSION = '1.0.0'
// ============================================================================
// v1 Legacy Pipeline
// ============================================================================
export const V1_SYSTEM_PROMPT = `Du bist ein DSGVO-Compliance-Experte und erstellst strukturierte Dokument-Entwuerfe.
Du MUSST immer im JSON-Format antworten mit einem "sections" Array.
Jede Section hat: id, title, content, schemaField.
Halte die Tiefe strikt am vorgegebenen Level.
Markiere fehlende Informationen mit [PLATZHALTER: Beschreibung].
Sprache: Deutsch.`
export function buildPromptForDocumentType(
documentType: ScopeDocumentType,
context: DraftContext,
instructions?: string
): string {
switch (documentType) {
case 'vvt':
return buildVVTDraftPrompt({ context, instructions })
case 'tom':
return buildTOMDraftPrompt({ context, instructions })
case 'dsfa':
return buildDSFADraftPrompt({ context, instructions })
case 'dsi':
return buildPrivacyPolicyDraftPrompt({ context, instructions })
case 'lf':
return buildLoeschfristenDraftPrompt({ context, instructions })
default:
return `## Aufgabe: Entwurf fuer ${documentType}
### Level: ${context.decisions.level}
### Tiefe: ${context.constraints.depthRequirements.depth}
### Erforderliche Inhalte:
${context.constraints.depthRequirements.detailItems.map((item, i) => `${i + 1}. ${item}`).join('\n')}
${instructions ? `### Anweisungen: ${instructions}` : ''}
Antworte als JSON mit "sections" Array.`
}
}
export async function handleV1Draft(body: Record<string, unknown>): Promise<NextResponse> {
const { documentType, draftContext, instructions, existingDraft } = body as {
documentType: ScopeDocumentType
draftContext: DraftContext
instructions?: string
existingDraft?: DraftRevision
}
const constraintCheck = constraintEnforcer.checkFromContext(documentType, draftContext)
if (!constraintCheck.allowed) {
return NextResponse.json({
draft: null,
constraintCheck,
tokensUsed: 0,
error: 'Constraint-Verletzung: ' + constraintCheck.violations.join('; '),
}, { status: 403 })
}
const ragCfg = DOCUMENT_RAG_CONFIG[documentType]
const ragContext = await queryRAG(ragCfg.query, 3, ragCfg.collection)
let v1SystemPrompt = V1_SYSTEM_PROMPT
if (ragContext) {
v1SystemPrompt += `\n\n## Relevanter Rechtskontext\n${ragContext}`
}
const draftPrompt = buildPromptForDocumentType(documentType, draftContext, instructions)
const messages = [
{ role: 'system', content: v1SystemPrompt },
...(existingDraft ? [{
role: 'assistant',
content: `Bisheriger Entwurf:\n${JSON.stringify(existingDraft.sections, null, 2)}`,
}] : []),
{ role: 'user', content: draftPrompt },
]
const ollamaResponse = await fetch(`${OLLAMA_URL}/api/chat`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model: LLM_MODEL,
messages,
stream: false,
think: false,
options: { temperature: 0.15, num_predict: 16384, num_ctx: 8192 },
format: 'json',
}),
signal: AbortSignal.timeout(180000),
})
if (!ollamaResponse.ok) {
return NextResponse.json(
{ error: `LLM nicht erreichbar (Status ${ollamaResponse.status})` },
{ status: 502 }
)
}
const result = await ollamaResponse.json()
const content = result.message?.content || ''
let sections: DraftSection[] = []
try {
const parsed = JSON.parse(content)
sections = (parsed.sections || []).map((s: Record<string, unknown>, i: number) => ({
id: String(s.id || `section-${i}`),
title: String(s.title || ''),
content: String(s.content || ''),
schemaField: s.schemaField ? String(s.schemaField) : undefined,
}))
} catch {
sections = [{ id: 'raw', title: 'Entwurf', content }]
}
const draft: DraftRevision = {
id: `draft-${Date.now()}`,
content: sections.map(s => `## ${s.title}\n\n${s.content}`).join('\n\n'),
sections,
createdAt: new Date().toISOString(),
instruction: instructions as string | undefined,
}
return NextResponse.json({
draft,
constraintCheck,
tokensUsed: result.eval_count || 0,
} satisfies DraftResponse)
}
// Re-export v2 handler for route.ts (backward compat — single import point)
export { handleV2Draft } from './draft-helpers-v2'