promptstat / prompt_card /preprocess.py
xxixx1028's picture
Deploy PromptStat — UI shell + MiniCPM4.1-8B + 4-LoRA hybrid (Modal)
dc9f530 verified
Raw
History Blame Contribute Delete
2.78 kB
"""Preprocess: per-message English filter, min-20 gate, most-recent-500 cap, focus helper.
The English filter is deterministic (langdetect seeded). Very short / low-signal strings that
langdetect cannot classify fall back to an ASCII-letter heuristic (assume English) so that terse
but valid English prompts ("fix this", "why?") are not dropped.
"""
from __future__ import annotations
import re
from dataclasses import dataclass, field
from langdetect import detect, DetectorFactory, LangDetectException
from .schema import Conversation, Turn, ROLE_USER
DetectorFactory.seed = 0
MIN_USER_MSGS = 20
CAP = 500
_LATIN = re.compile(r"[A-Za-z]")
_NON_ASCII = re.compile(r"[^\x00-\x7f]")
class InsufficientData(Exception):
"""Raised when there are fewer than MIN_USER_MSGS English user messages to score."""
@dataclass
class Prepared:
conversations: list[Conversation] # English-only turns
user_prompts: list[str] = field(default_factory=list) # flat, most-recent CAP
def is_english(text: str) -> bool:
t = (text or "").strip()
if not t:
return False
# Heavy non-ASCII content (CJK, etc.) is clearly not English.
if _NON_ASCII.search(t) and len(_NON_ASCII.findall(t)) >= max(3, len(t) * 0.2):
return False
try:
return detect(t) == "en"
except LangDetectException:
# Too short for langdetect; keep if it contains Latin letters.
return bool(_LATIN.search(t))
def _filter_english(conv: Conversation) -> Conversation:
turns = [t for t in conv.turns if is_english(t.text)]
return Conversation(provider=conv.provider, created_at=conv.created_at, turns=turns)
def multi_message_sessions(conversations: list[Conversation]) -> list[list[str]]:
"""User-prompt lists per conversation, keeping only sessions with >=2 user prompts
(focus needs at least two messages to measure coherence)."""
sessions = []
for c in conversations:
prompts = [t.text for t in c.turns if t.role == ROLE_USER]
if len(prompts) >= 2:
sessions.append(prompts)
return sessions
def preprocess(
conversations: list[Conversation],
*,
cap: int = CAP,
min_user_msgs: int = MIN_USER_MSGS,
) -> Prepared:
filtered = [_filter_english(c) for c in conversations]
filtered = [c for c in filtered if c.turns]
user_prompts = [t.text for c in filtered for t in c.turns if t.role == ROLE_USER]
if len(user_prompts) < min_user_msgs:
raise InsufficientData(
f"need >= {min_user_msgs} English user messages, found {len(user_prompts)}"
)
# most-recent cap (assumes chronological order within/across conversations)
user_prompts = user_prompts[-cap:]
return Prepared(conversations=filtered, user_prompts=user_prompts)