diff --git a/control-pipeline/services/control_generator.py b/control-pipeline/services/control_generator.py index 1f99ca1..f65ec66 100644 --- a/control-pipeline/services/control_generator.py +++ b/control-pipeline/services/control_generator.py @@ -1526,9 +1526,22 @@ Gib ein JSON-Array zurueck mit GENAU {len(chunks)} Elementen. Fuer Aspekte ohne final: list[Optional[GeneratedControl]] = [] for i in range(len(batch_items)): control = all_controls.get(i) - if not control or (not control.title and not control.objective): + # Filter empty or invalid controls (LLM returned None/empty) + if not control: final.append(None) continue + title_invalid = not control.title or control.title.strip().lower() in ("none", "null", "") + obj_invalid = not control.objective or control.objective.strip().lower() in ("none", "null", "") + if title_invalid and obj_invalid: + logger.warning("Leerer Control gefiltert (title=%s, objective=%s) — wird nicht gespeichert", + control.title, control.objective) + final.append(None) + continue + # Clean up "None" strings from LLM + if title_invalid: + control.title = control.objective[:120] if control.objective else "Unbenannt" + if obj_invalid: + control.objective = control.title if control.release_state == "too_close": final.append(control) @@ -1931,6 +1944,14 @@ Kategorien: {CATEGORY_LIST_STR}""" def _store_control(self, control: GeneratedControl, job_id: str) -> Optional[str]: """Persist a generated control to DB. Returns the control UUID or None.""" + # Pre-store quality guard — reject empty/invalid controls + if not control.title or control.title.strip().lower() in ("none", "null", ""): + logger.warning("Rejected control with empty/None title: %s", control.control_id) + return None + if not control.objective or control.objective.strip().lower() in ("none", "null", ""): + logger.warning("Rejected control with empty/None objective: %s — %s", control.control_id, control.title) + return None + try: # Get framework UUID fw_result = self.db.execute(