fix: UCCA assessment — send boolean intake flags, flatten nested response, map risk→escalation
Some checks failed
Build + Deploy / build-admin-compliance (push) Successful in 1m56s
Build + Deploy / build-backend-compliance (push) Successful in 3m6s
Build + Deploy / build-ai-sdk (push) Successful in 45s
Build + Deploy / build-developer-portal (push) Successful in 1m2s
Build + Deploy / build-tts (push) Successful in 1m19s
Build + Deploy / build-document-crawler (push) Successful in 34s
Build + Deploy / build-dsms-gateway (push) Successful in 21s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / loc-budget (push) Failing after 16s
CI / secret-scan (push) Has been skipped
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Successful in 2m35s
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / test-go (push) Successful in 48s
CI / test-python-backend (push) Successful in 1m35s
CI / test-python-document-crawler (push) Successful in 26s
CI / test-python-dsms-gateway (push) Successful in 25s
CI / validate-canonical-controls (push) Successful in 20s
Build + Deploy / trigger-orca (push) Successful in 3m15s
Some checks failed
Build + Deploy / build-admin-compliance (push) Successful in 1m56s
Build + Deploy / build-backend-compliance (push) Successful in 3m6s
Build + Deploy / build-ai-sdk (push) Successful in 45s
Build + Deploy / build-developer-portal (push) Successful in 1m2s
Build + Deploy / build-tts (push) Successful in 1m19s
Build + Deploy / build-document-crawler (push) Successful in 34s
Build + Deploy / build-dsms-gateway (push) Successful in 21s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / loc-budget (push) Failing after 16s
CI / secret-scan (push) Has been skipped
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Successful in 2m35s
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / test-go (push) Successful in 48s
CI / test-python-backend (push) Successful in 1m35s
CI / test-python-document-crawler (push) Successful in 26s
CI / test-python-dsms-gateway (push) Successful in 25s
CI / validate-canonical-controls (push) Successful in 20s
Build + Deploy / trigger-orca (push) Successful in 3m15s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -157,34 +157,87 @@ async def _classify(client: httpx.AsyncClient, text: str) -> str:
|
||||
|
||||
|
||||
async def _assess(client: httpx.AsyncClient, text: str, classification: str) -> dict:
|
||||
"""Run UCCA assessment via SDK."""
|
||||
"""Run UCCA assessment via SDK. Returns flattened result dict."""
|
||||
try:
|
||||
# UCCA expects boolean intake flags, not string categories
|
||||
resp = await client.post(f"{SDK_URL}/sdk/v1/ucca/assess", headers=SDK_HEADERS, json={
|
||||
"use_case_text": text[:3000],
|
||||
"domain": classification,
|
||||
"data_categories": ["personal_data", "tracking", "cookies", "third_party_sharing"],
|
||||
"data_types": {
|
||||
"personal_data": True,
|
||||
"customer_data": True,
|
||||
"location_data": "tracking" in text.lower() or "standort" in text.lower(),
|
||||
"images": False,
|
||||
"biometric_data": "biometrisch" in text.lower(),
|
||||
"minor_data": "kinder" in text.lower() or "minderjährig" in text.lower(),
|
||||
},
|
||||
"purpose": {
|
||||
"marketing": "werbung" in text.lower() or "marketing" in text.lower(),
|
||||
"analytics": "analyse" in text.lower() or "analytics" in text.lower(),
|
||||
"profiling": "profil" in text.lower() or "personalis" in text.lower(),
|
||||
"automation": False,
|
||||
"customer_support": False,
|
||||
},
|
||||
"automation": "partially_automated",
|
||||
"outputs": {
|
||||
"content_generation": False,
|
||||
"recommendations_to_users": "empfehl" in text.lower(),
|
||||
"data_export": "export" in text.lower() or "uebertrag" in text.lower(),
|
||||
},
|
||||
})
|
||||
return resp.json()
|
||||
data = resp.json()
|
||||
# Flatten: UCCA wraps result under "assessment" and "result"
|
||||
assessment = data.get("assessment", data.get("result", data))
|
||||
result = data.get("result", {})
|
||||
return {
|
||||
"risk_level": assessment.get("risk_level", result.get("risk_level", "unknown")),
|
||||
"risk_score": assessment.get("risk_score", result.get("risk_score", 0)),
|
||||
"escalation_level": _risk_to_escalation(assessment.get("risk_level", "")),
|
||||
"triggered_rules": assessment.get("triggered_rules", result.get("triggered_rules", [])),
|
||||
"required_controls": assessment.get("required_controls", result.get("required_controls", [])),
|
||||
"summary": result.get("summary", ""),
|
||||
"recommendation": result.get("recommendation", ""),
|
||||
"dsfa_recommended": assessment.get("dsfa_recommended", False),
|
||||
}
|
||||
except Exception as e:
|
||||
logger.warning("Assessment failed: %s", e)
|
||||
return {"risk_level": "unknown", "risk_score": 0, "escalation_level": "E0"}
|
||||
|
||||
|
||||
def _risk_to_escalation(risk_level: str) -> str:
|
||||
"""Map UCCA risk level to escalation level."""
|
||||
mapping = {
|
||||
"MINIMAL": "E0",
|
||||
"LIMITED": "E1",
|
||||
"HIGH": "E2",
|
||||
"UNACCEPTABLE": "E3",
|
||||
}
|
||||
return mapping.get(risk_level.upper() if risk_level else "", "E0")
|
||||
|
||||
|
||||
def _build_summary(url: str, classification: str, assessment: dict, role: str) -> str:
|
||||
"""Build a German manager summary."""
|
||||
risk = assessment.get("risk_level", "unbekannt")
|
||||
score = assessment.get("risk_score", 0)
|
||||
findings = assessment.get("triggered_rules", [])
|
||||
controls = assessment.get("required_controls", [])
|
||||
recommendation = assessment.get("recommendation", "")
|
||||
dsfa = assessment.get("dsfa_recommended", False)
|
||||
|
||||
findings_text = "\n".join(f"- {f}" for f in findings[:5]) if findings else "Keine"
|
||||
controls_text = "\n".join(f"- {c}" for c in controls[:5]) if controls else "Keine"
|
||||
|
||||
return (
|
||||
f"Dokumenttyp: {classification}\n"
|
||||
f"Quelle: {url}\n"
|
||||
f"Risikobewertung: {risk} ({score}/100)\n"
|
||||
f"Zustaendig: {role}\n\n"
|
||||
f"Findings:\n{findings_text}\n\n"
|
||||
f"Erforderliche Massnahmen:\n{controls_text}"
|
||||
)
|
||||
parts = [
|
||||
f"Dokumenttyp: {classification}",
|
||||
f"Quelle: {url}",
|
||||
f"Risikobewertung: {risk} ({score}/100)",
|
||||
f"Zustaendig: {role}",
|
||||
f"DSFA empfohlen: {'Ja' if dsfa else 'Nein'}",
|
||||
"",
|
||||
f"Findings:\n{findings_text}",
|
||||
"",
|
||||
f"Erforderliche Massnahmen:\n{controls_text}",
|
||||
]
|
||||
if recommendation:
|
||||
parts.extend(["", f"Empfehlung: {recommendation}"])
|
||||
return "\n".join(parts)
|
||||
|
||||
Reference in New Issue
Block a user