feat: Tender-Analyse Pipeline — Upload, Extraction, Control-Matching

Phase 3 des Payment Compliance Moduls:
1. Backend: Tender Upload + LLM Requirement Extraction + Control Matching
   - DB Migration 025 (tender_analyses Tabelle)
   - TenderHandlers: Upload, Extract, Match, List, Get (5 Endpoints)
   - LLM-Extraktion via Anthropic API mit Keyword-Fallback
   - Control-Matching mit Domain-Bonus + Keyword-Overlap Relevance
2. Frontend: Dritter Tab "Ausschreibung" in /sdk/payment-compliance
   - PDF/TXT/Word Upload mit Drag-Area
   - Automatische Analyse-Pipeline (Upload → Extract → Match)
   - Ergebnis-Dashboard: Abgedeckt/Teilweise/Luecken
   - Requirement-by-Requirement Matching mit Control-IDs + Relevanz%
   - Gap-Beschreibung fuer nicht-gematchte Requirements
   - Analyse-Historie mit Klick-to-Detail

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-04-13 09:35:46 +02:00
parent 38d3d24121
commit 4fcb842a92
7 changed files with 854 additions and 3 deletions

View File

@@ -0,0 +1,28 @@
import { NextRequest, NextResponse } from 'next/server'
const SDK_URL = process.env.SDK_URL || 'http://ai-compliance-sdk:8090'
export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
try {
const { id } = await params
const resp = await fetch(`${SDK_URL}/sdk/v1/payment-compliance/tender/${id}`)
return NextResponse.json(await resp.json())
} catch {
return NextResponse.json({ error: 'Failed' }, { status: 500 })
}
}
export async function POST(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
try {
const { id } = await params
const { searchParams } = new URL(request.url)
const action = searchParams.get('action') || 'extract'
const resp = await fetch(`${SDK_URL}/sdk/v1/payment-compliance/tender/${id}/${action}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
})
return NextResponse.json(await resp.json(), { status: resp.status })
} catch {
return NextResponse.json({ error: 'Failed' }, { status: 500 })
}
}

View File

@@ -0,0 +1,30 @@
import { NextRequest, NextResponse } from 'next/server'
const SDK_URL = process.env.SDK_URL || 'http://ai-compliance-sdk:8090'
export async function GET(request: NextRequest) {
try {
const tenantId = request.headers.get('x-tenant-id') || '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e'
const resp = await fetch(`${SDK_URL}/sdk/v1/payment-compliance/tender`, {
headers: { 'X-Tenant-ID': tenantId },
})
return NextResponse.json(await resp.json())
} catch {
return NextResponse.json({ error: 'Failed' }, { status: 500 })
}
}
export async function POST(request: NextRequest) {
try {
const tenantId = request.headers.get('x-tenant-id') || '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e'
const formData = await request.formData()
const resp = await fetch(`${SDK_URL}/sdk/v1/payment-compliance/tender/upload`, {
method: 'POST',
headers: { 'X-Tenant-ID': tenantId },
body: formData,
})
return NextResponse.json(await resp.json(), { status: resp.status })
} catch {
return NextResponse.json({ error: 'Upload failed' }, { status: 500 })
}
}