GitHub Action
Sync from GitHub
ef78361
Raw
History Blame Contribute Delete
5.03 kB
"""Utility functions for Gen3 Nudge Detection Dashboard."""
# Confidence tier thresholds
CONFIDENCE_HIGH_THRESHOLD = 0.85
CONFIDENCE_MEDIUM_THRESHOLD = 0.70
def get_confidence_tier(confidence: float | None) -> tuple[str, str, str]:
"""Get confidence tier info for nudge candidates.
Returns:
Tuple of (tier, emoji, description)
"""
if confidence is None:
return "LOW", "🟒", "μ˜€νƒ κ°€λŠ₯μ„±"
if confidence >= CONFIDENCE_HIGH_THRESHOLD:
return "HIGH", "πŸ”΄", "ν™•μ‹€ν•œ λ„›μ§€ λŒ€μƒ"
elif confidence >= CONFIDENCE_MEDIUM_THRESHOLD:
return "MEDIUM", "🟑", "κ²€ν†  ν•„μš”"
return "LOW", "🟒", "μ˜€νƒ κ°€λŠ₯μ„±"
def truncate_text(text: str | None, max_length: int, suffix: str = "...") -> str:
"""Truncate text to max_length with suffix."""
if not text:
return "N/A"
if len(text) <= max_length:
return text
return text[:max_length] + suffix
def format_brands_list(brands: list[str] | None) -> str:
"""Format list of brands for display."""
if not brands:
return "N/A"
return ", ".join(brands)
def highlight_evidence_spans(text: str, evidence_spans: list[dict] | None) -> str:
"""Highlight evidence spans in text using HTML.
Args:
text: Full answer text
evidence_spans: List of evidence span dicts with text, start, end, type
Returns:
HTML string with highlighted spans
"""
if not evidence_spans or not text:
return text or ""
# Sort spans by start position (descending) to avoid index shifting
sorted_spans = sorted(
[s for s in evidence_spans if s.get("text")],
key=lambda s: s.get("start", 0) if s.get("start") is not None else -1,
reverse=True,
)
result = text
for span in sorted_spans:
span_text = span.get("text", "")
span_type = span.get("type", "negative")
start = span.get("start")
end = span.get("end")
# Color by type
color_map = {
"negative": "#FF6B6B",
"positive": "#51CF66",
"neutral": "#748FFC",
"comparison": "#FAB005",
"hallucination": "#ADB5BD",
"category_general": "#9775FA",
}
color = color_map.get(span_type, "#ADB5BD")
mark_style = f'background-color: {color}; padding: 2px 4px; border-radius: 3px;'
mark_style = f'background-color: {color}; padding: 2px 4px; border-radius: 3px;'
if start is not None and end is not None and 0 <= start < end <= len(result):
# Use exact positions
before = result[:start]
highlighted = f'<mark style="{mark_style}">{result[start:end]}</mark>'
after = result[end:]
result = before + highlighted + after
elif span_text and span_text in result:
# Fallback: find text in result
highlighted = f'<mark style="{mark_style}">{span_text}</mark>'
result = result.replace(span_text, highlighted, 1)
elif span_text and "..." in span_text:
# Ellipsis fallback: LLM truncated the evidence with "..."
# Split into fragments and highlight each one found in the text
fragments = [f.strip() for f in span_text.split("...") if f.strip()]
for frag in fragments:
if frag in result:
highlighted = f'<mark style="{mark_style}">{frag}</mark>'
result = result.replace(frag, highlighted, 1)
return result
def get_llm_tier_badge(adjusted_tier: str | None, is_negative: bool | None) -> tuple[str, str]:
"""Get badge info for LLM verification result.
Returns:
Tuple of (badge_text, badge_color)
"""
if adjusted_tier is None:
return "미검증", "gray"
if adjusted_tier == "NONE" or is_negative is False:
return "βœ… μ˜€νƒ (False Positive)", "green"
tier_map = {
"HIGH": ("πŸ”΄ λΆ€μ • 확인 (HIGH)", "red"),
"MEDIUM": ("🟑 λΆ€μ • 확인 (MEDIUM)", "orange"),
"LOW": ("🟒 λΆ€μ • 확인 (LOW)", "blue"),
}
return tier_map.get(adjusted_tier, ("확인됨", "gray"))
def get_feedback_reason_label(reason: str | None) -> str:
"""Get human-readable label for feedback wrong_reason."""
reason_labels = {
"actually_positive": "μ‹€μ œλ‘œλŠ” 긍정적인 λ‚΄μš©μž…λ‹ˆλ‹€",
"actually_neutral": "μ‹€μ œλ‘œλŠ” 쀑립적인 λ‚΄μš©μž…λ‹ˆλ‹€",
"wrong_evidence": "κ·Όκ±° λ¬Έμž₯이 잘λͺ» μΆ”μΆœλ˜μ—ˆμŠ΅λ‹ˆλ‹€",
"context_missing": "λ§₯락이 λΉ μ Έμ„œ μ˜€ν•΄κ°€ μžˆμŠ΅λ‹ˆλ‹€",
"wrong_brand": "λΈŒλžœλ“œκ°€ 잘λͺ» μΈμ‹λ˜μ—ˆμŠ΅λ‹ˆλ‹€",
"other": "기타",
}
return reason_labels.get(reason, reason or "")
def get_feedback_type_emoji(feedback_type: str | None) -> str:
"""Get emoji for feedback type."""
emoji_map = {
"correct": "πŸ‘",
"wrong": "πŸ‘Ž",
"ambiguous": "πŸ€”",
}
return emoji_map.get(feedback_type, "")