"""Phase S: 생성 HTML 콘텐츠 검증 + 재시도 루프.
생성기(html_generator)와 완전히 분리된 독립 검증.
코드 기반 검증을 먼저, LLM 검증은 코드가 못 잡는 것만.
검증 계층:
Layer 1: 텍스트 보존 검증 (코드, $0)
Layer 2: 금지 콘텐츠 검증 (코드, $0)
Layer 3: 구조 검증 (코드, $0)
Layer 4: 오버플로 검증 (Selenium, $0) — slide_measurer.py 재사용
Layer 5: 시각 품질 검증 (Opus 비전, $$) — kei_client.py 재사용
"""
from __future__ import annotations
import logging
import re
from dataclasses import dataclass, field
from difflib import SequenceMatcher
from html.parser import HTMLParser
logger = logging.getLogger(__name__)
# ═══════════════════════════════════════════════════════════
# 데이터 구조
# ═══════════════════════════════════════════════════════════
@dataclass
class VerificationResult:
"""단일 영역의 검증 결과."""
passed: bool
area_name: str
checks: dict[str, bool] = field(default_factory=dict)
score: float = 0.0
errors: list[str] = field(default_factory=list)
warnings: list[str] = field(default_factory=list)
# ═══════════════════════════════════════════════════════════
# HTML 텍스트 추출
# ═══════════════════════════════════════════════════════════
class _TextExtractor(HTMLParser):
"""HTML에서 가시 텍스트만 추출.