세션 평가·라이브코치·교수자 분석 라운드 마감 + 문서 정리 + 코드품질 리팩터
- 누적 작업트리 커밋: 회기 평가 복구·durable 저장, 라이브 코치 이력/근거, 교수자 학생분석, 음성 비언어 메타, PII 마스킹, 운영 티켓/헬스 등 - 문서: 완료 기록 docs/archive/ 냉동 보관, docs/ 단일 인덱스(docs/README.md)+통합 TODO(docs/TODO.md)로 정리 - 리팩터(행위 보존): Stage enum SSOT(taxonomy 소유·state_machine re-export), store recent/masked_turns 중복 제거, speaker_ko_label 단일 헬퍼, _list_sessions N+1 제거(state/turns 배치 + 턴평가 하이드레이션 배치) - 검증: 백엔드 pytest 352 passed, _list_sessions E2E chromium-single-run 2 passed
This commit is contained in:
parent
7c41c3ce79
commit
778e8526d4
108 changed files with 6457 additions and 455 deletions
|
|
@ -111,6 +111,23 @@ def normalize_json_value(value: Any) -> Any:
|
|||
return value
|
||||
|
||||
|
||||
def export_safe_supervisor_comments(value: Any) -> list[dict[str, Any]]:
|
||||
comments = normalize_json_value(value or [])
|
||||
if not isinstance(comments, list):
|
||||
return []
|
||||
safe_comments: list[dict[str, Any]] = []
|
||||
for item in comments:
|
||||
if not isinstance(item, Mapping):
|
||||
continue
|
||||
safe_item: dict[str, Any] = {}
|
||||
for key in ("kind", "intent_deviation"):
|
||||
if key in item:
|
||||
safe_item[key] = json_safe(normalize_json_value(item[key]))
|
||||
if safe_item:
|
||||
safe_comments.append(safe_item)
|
||||
return safe_comments
|
||||
|
||||
|
||||
def _redacted_sample(kind: str, value: str) -> str:
|
||||
if kind == "email" and "@" in value:
|
||||
return f"<email:{value.rsplit('@', 1)[1].lower()}>"
|
||||
|
|
@ -180,7 +197,7 @@ def build_dataset_record(
|
|||
"techniques": json_safe(normalize_json_value(row.get("techniques") or [])),
|
||||
"client_states": json_safe(normalize_json_value(row.get("client_states") or [])),
|
||||
"feedback_scores": json_safe(normalize_json_value(row.get("feedback_scores") or [])),
|
||||
"supervisor_comments": json_safe(normalize_json_value(row.get("supervisor_comments") or [])),
|
||||
"supervisor_comments": export_safe_supervisor_comments(row.get("supervisor_comments") or []),
|
||||
"source_refs": {
|
||||
"session_started_at": json_safe(row.get("session_started_at") or row.get("started_at")),
|
||||
"export_manifest_id": export_manifest_id,
|
||||
|
|
@ -393,6 +410,13 @@ def validate_manifest_gate(manifest: Mapping[str, Any]) -> None:
|
|||
errors.append("kappa must be >= 0.70")
|
||||
if (agreement.get("icc") or 0) < 0.75:
|
||||
errors.append("ICC must be >= 0.75")
|
||||
selection_criteria = manifest.get("selection_criteria") or {}
|
||||
if not isinstance(selection_criteria, Mapping) or selection_criteria.get("include_withdrawn") is not False:
|
||||
errors.append("include_withdrawn must be false")
|
||||
consent_scope = manifest.get("consent_scope") or {}
|
||||
allowed_uses = consent_scope.get("allowed_uses") if isinstance(consent_scope, Mapping) else None
|
||||
if not isinstance(allowed_uses, list) or "recursive_learning_seed" not in allowed_uses:
|
||||
errors.append("recursive_learning_seed consent scope is required")
|
||||
for key in ("data_steward", "legal_or_privacy_reviewer", "technical_operator", "approved_at"):
|
||||
if not str(approvals.get(key) or "").strip():
|
||||
errors.append(f"approval missing: {key}")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue