90a70c8404
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / iace-gt-coverage (push) Has been skipped
CI / test-python-backend (push) Has been skipped
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
CI / detect-changes (push) Successful in 7s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / build-sha-integrity (push) Successful in 5s
CI / validate-canonical-controls (push) Successful in 4s
CI / loc-budget (push) Successful in 17s
CI / go-lint (push) Has been skipped
CI / nodejs-build (push) Successful in 3m2s
CI / test-go (push) Has been skipped
Die Drafting-Engine (Dokument-Entwurf, v2-Pipeline, Validierung, Drafting-Chat, Vendor-Vertragspruefung) war auf prod doppelt tot: - RAG ueber bp-core-rag-service:8097 (existiert auf prod nicht) - LLM ueber OLLAMA_URL/api/chat mit qwen2.5vl (prod = ollama-embed, kein Chat-Modell) Fix (analog zum Compliance-Advisor): - rag-query.ts -> ai-compliance-sdk /sdk/v1/rag/search (bge-m3, prod-erreichbar). - Neue lib/sdk/drafting-engine/llm-cascade.ts: OVH/LiteLLM (gpt-oss-120b) zuerst, Ollama als Dev-Fallback; cascadeComplete (JSON) + cascadeStream. Das Backend nutzt OVH+JSON bereits erfolgreich auf prod (extract-datasheet). - 5 Aufrufstellen (draft-helpers, draft-helpers-v2, validate, chat, vendor-review) auf die Kaskade umgestellt; keine direkten Ollama-Calls mehr. - Tests: llm-cascade + rag-query aktualisiert. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
152 lines
5.4 KiB
TypeScript
152 lines
5.4 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'
|
|
import { cascadeComplete } from '@/lib/sdk/drafting-engine/llm-cascade'
|
|
|
|
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 = ragCfg ? await queryRAG(ragCfg.query, 3, ragCfg.collection) : null
|
|
|
|
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 llm = await cascadeComplete(messages, {
|
|
json: true,
|
|
temperature: 0.15,
|
|
maxTokens: 16384,
|
|
timeoutMs: 180000,
|
|
})
|
|
|
|
if (!llm) {
|
|
return NextResponse.json(
|
|
{ error: 'LLM nicht erreichbar (weder OVH noch Ollama)' },
|
|
{ status: 502 }
|
|
)
|
|
}
|
|
|
|
const content = llm.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: llm.tokensUsed,
|
|
} satisfies DraftResponse)
|
|
}
|
|
|
|
// Re-export v2 handler for route.ts (backward compat — single import point)
|
|
export { handleV2Draft } from './draft-helpers-v2'
|