fix(stage11): resolve subagent merge conflicts — audio models, vision fix, test fixes

This commit is contained in:
NSCT Agent
2026-08-25 17:29:17 +00:00
parent d76d48a0ee
commit 4b8ae6a41a
5 changed files with 1125 additions and 114 deletions

View File

@@ -208,13 +208,15 @@ def _extract_claims(text: str, segment_type: str) -> list[Claim]:
claim = Claim(
text=sentence,
segment_type=norm_segment,
provenance={
"audio_source": "stt_service",
"extraction_method": "heuristic_sentence_split",
"segment_type": segment_type,
"total_sentences": len(sentences),
},
segment_type=segment_type if segment_type in (
"interview", "podcast", "pressekonferenz", "meeting", "other"
) else "other",
confidence=0.65,
)
claims.append(claim)

View File

@@ -181,6 +181,13 @@ class AudioClaimSchema(BaseModel):
raise ValueError("source_url darf nicht leer sein")
return v
@field_validator("speaker_id")
@classmethod
def speaker_id_not_empty(cls, v: str) -> str:
if not v.strip():
raise ValueError("speaker_id darf nicht leer sein")
return v
@field_validator("timestamp_end")
@classmethod
def end_after_start(cls, v: float, info) -> float:
@@ -234,7 +241,6 @@ class AudioReportSchema(BaseModel):
)
source_url: str | None = Field(
default=None,
min_length=1,
description="URL der Audio-Quelle.",
)
research_run_id: str | None = Field(
@@ -281,17 +287,14 @@ class AudioRequestSchema(BaseModel):
research_run_id: str = Field(
...,
min_length=1,
description="UUID des Research-Runs.",
)
audio_file_url: str | None = Field(
default=None,
min_length=1,
description="URL der Audio-Datei (MP3, WAV, OGG, etc.).",
)
audio_bytes_b64: str | None = Field(
default=None,
min_length=1,
description="Base64-codiertes Audio-Bytes (alternativ zu URL).",
)
segment_type: AudioSegmentType = Field(
@@ -300,10 +303,16 @@ class AudioRequestSchema(BaseModel):
)
source_id: str | None = Field(
default=None,
min_length=1,
description="UUID der Quelle (source_id) zur Provenance.",
)
@field_validator("research_run_id")
@classmethod
def research_run_id_not_empty(cls, v: str) -> str:
if not v.strip():
raise ValueError("research_run_id darf nicht leer sein")
return v
@field_validator("audio_file_url")
@classmethod
def audio_file_url_not_empty(cls, v: str | None) -> str | None:

View File

@@ -207,16 +207,24 @@ class VisionReportSchema(BaseModel):
def summary_not_political(cls, v: str) -> str:
if not v.strip():
return v
import re
forbidden = re.compile(
r"((Regierung|Bundesregierung)\s+(muss|sollte)\s+(handeln|unterstützen)|"
r"(sollte\s+(Regierung|Bundesregierung)\s+(handeln|unterstützen)|"
r"muss\s+(geändert|eingesetzt|gestürzt))",
re.IGNORECASE,
)
if forbidden.search(v):
raise ValueError("VisionReport darf keine politische Empfehlung enthalten")
# Check for political recommendations in the summary text
lower = v.lower()
forbidden_patterns = [
"regierung sollte handeln",
"regierung sollte unterstützen",
"bundesregierung sollte handeln",
"bundesregierung sollte unterstützen",
"regierung muss handeln",
"regierung muss ändern",
"bundesregierung muss handeln",
"bundesregierung muss ändern",
"muss geändert",
"muss eingesetzt",
"muss gestürzt",
]
for pattern in forbidden_patterns:
if pattern in lower:
raise ValueError("VisionReport darf keine politische Empfehlung enthalten")
return v
model_config = {"frozen": True}
@@ -273,6 +281,7 @@ class VisionRequestSchema(BaseModel):
if not v.strip():
raise ValueError("source_id darf nicht nur aus Whitespaces bestehen")
return v
capture_type: VisionCaptureType = Field(
default=VisionCaptureType.RAW_IMAGE,
description="Art der visuellen Erfassung.",