41 lines
1.4 KiB
Python
41 lines
1.4 KiB
Python
"""Parse and normalize the field-assist model response.
|
|
|
|
Pure module: no FastAPI imports and no HTTPException.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
|
|
from job_assist.prompt import LIST_FIELDS, SINGLE_LINE_FIELDS
|
|
|
|
MAX_SUGGESTION_CHARS = 6000
|
|
|
|
# Leading bullet/number markers the model may emit despite the prompt.
|
|
_BULLET = re.compile(r"^\s*(?:[-*•·]+|\d+[.)])\s+")
|
|
|
|
|
|
def parse_assist_response(data, field) -> str:
|
|
"""The suggestion string, normalized to the field's shape. Raises RuntimeError."""
|
|
if not isinstance(data, dict):
|
|
raise RuntimeError("assist response is not an object")
|
|
suggestion = data.get("suggestion")
|
|
if not isinstance(suggestion, str) or not suggestion.strip():
|
|
raise RuntimeError("assist response has no suggestion")
|
|
|
|
text = suggestion.replace("\r\n", "\n").replace("\r", "\n").strip()
|
|
|
|
if field in SINGLE_LINE_FIELDS:
|
|
text = " ".join(text.split())
|
|
elif field in LIST_FIELDS:
|
|
# The form splits these on newline (Jobs.jsx splitLines), so strip any
|
|
# bullet markers and keep one item per line.
|
|
lines = [_BULLET.sub("", line).strip() for line in text.split("\n")]
|
|
text = "\n".join(line for line in lines if line)
|
|
if not text:
|
|
raise RuntimeError("assist response emptied after normalization")
|
|
else:
|
|
text = re.sub(r"\n{3,}", "\n\n", text)
|
|
|
|
return text[:MAX_SUGGESTION_CHARS].strip()
|