fix(chat): map customer/seller roles for OpenAI-compat LLM; re-contact = mid-chat time-lapse
- llm.complete_conversation now maps internal roles (customer->assistant, seller->user,
system->system) before the API call — fixes 'Unknown role: customer' (501).
- Re-contact personality: the customer now chats normally, at turn 2 goes quiet and a
system time-lapse note is shown ('⏳ ผ่านไป 2-3 สัปดาห์...'), then re-engages warmer —
instead of 'pretending you asked before' at start. Driven by persona.recontact trait.
All 9 backend suites pass. Rebuilt dist.
This commit is contained in:
@@ -257,6 +257,20 @@ def send_message(gid: str, pid: str):
|
|||||||
elif mood >= 1:
|
elif mood >= 1:
|
||||||
internal["signals"].append({"turn": internal["turns"], "mood": mood, "type": "warm"})
|
internal["signals"].append({"turn": internal["turns"], "mood": mood, "type": "warm"})
|
||||||
|
|
||||||
|
# Re-contact persona behavior: after enough info is exchanged (turn 2), the customer
|
||||||
|
# goes quiet, a time-lapse system note is shown, and the customer re-engages warmer.
|
||||||
|
if persona.get("recontact") and not internal.get("recontact_done") and internal["turns"] >= 2:
|
||||||
|
unit = "สัปดาห์" if slocale != "en" else "weeks"
|
||||||
|
sys_txt = (
|
||||||
|
f"⏳ ผ่านไป 2-3 {unit} ... ลูกค้าที่เคยสอบถามไปเงียบไประยะหนึ่ง ตอนนี้กลับมาติดต่ออีกครั้ง (พร้อมตัดสินใจมากขึ้น)"
|
||||||
|
if slocale != "en"
|
||||||
|
else "⏳ 2-3 weeks later ... the customer who asked earlier went quiet; now they re-contact, more ready to decide."
|
||||||
|
)
|
||||||
|
messages.append({"role": "system", "text": sys_txt})
|
||||||
|
internal["recontact_done"] = True
|
||||||
|
# Save the time-lapse note immediately so the UI shows it even if send ends here.
|
||||||
|
s["sessions"].update(session["id"], messages=messages, internal=internal)
|
||||||
|
|
||||||
# Decision by the persona ends the session (one-shot lock).
|
# Decision by the persona ends the session (one-shot lock).
|
||||||
decision = meta.get("decision")
|
decision = meta.get("decision")
|
||||||
if decision in ("buy", "walk"):
|
if decision in ("buy", "walk"):
|
||||||
|
|||||||
@@ -116,12 +116,27 @@ class LLMClient:
|
|||||||
temperature: float = 0.6,
|
temperature: float = 0.6,
|
||||||
max_tokens: int = 1200,
|
max_tokens: int = 1200,
|
||||||
) -> str:
|
) -> str:
|
||||||
|
# Normalize internal role labels (customer/seller) to the roles an OpenAI-compatible
|
||||||
|
# chat endpoint accepts: system/user/assistant. customer=assistant (the persona/LLM),
|
||||||
|
# seller=user (the trainee). Anything else maps to a safe default.
|
||||||
|
api_messages = []
|
||||||
|
for m in messages:
|
||||||
|
role = (m.get("role") or "").lower()
|
||||||
|
if role == "system":
|
||||||
|
mapped = "system"
|
||||||
|
elif role in ("customer", "assistant"):
|
||||||
|
mapped = "assistant"
|
||||||
|
elif role in ("seller", "user"):
|
||||||
|
mapped = "user"
|
||||||
|
else:
|
||||||
|
mapped = "user"
|
||||||
|
api_messages.append({"role": mapped, "content": m.get("text") or m.get("content") or ""})
|
||||||
try:
|
try:
|
||||||
resp = self.client.chat.completions.create(
|
resp = self.client.chat.completions.create(
|
||||||
model=self.model,
|
model=self.model,
|
||||||
temperature=temperature,
|
temperature=temperature,
|
||||||
max_tokens=max_tokens,
|
max_tokens=max_tokens,
|
||||||
messages=messages,
|
messages=api_messages,
|
||||||
)
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
raise LLMError(f"LLM call failed: {exc}") from exc
|
raise LLMError(f"LLM call failed: {exc}") from exc
|
||||||
|
|||||||
Reference in New Issue
Block a user