Spaces:
Sleeping
Sleeping
| """ | |
| classify_character_inquiry.py | |
| Detect when the user is asking about other available characters. | |
| LLM-only β no keyword fast-path (keywords caused too many false positives). | |
| Returns: | |
| { | |
| "is_character_inquiry": bool, | |
| "inquiry_type": "style" | "pragmatic" | "general" | None | |
| style β user wants someone with a different conversational style | |
| ("someone more direct", "less patient", "more sarcastic") | |
| pragmatic β user wants a character better suited to structured guidance | |
| ("someone who explains step by step", "someone more patient") | |
| general β generic "who else is there" / "what other characters" | |
| } | |
| """ | |
| import json | |
| import re | |
| from typing import Dict, Any, List | |
| from llm_client import client | |
| from config import OPENAI_CLASSIFIER_MODEL | |
| def _parse_json(raw: str, fallback: Any) -> Any: | |
| m = re.search(r"\{.*\}", raw, re.S) | |
| if m: | |
| try: | |
| return json.loads(m.group(0)) | |
| except json.JSONDecodeError: | |
| pass | |
| try: | |
| return json.loads(raw) | |
| except json.JSONDecodeError: | |
| return fallback | |
| def classify_character_inquiry( | |
| user_msg: str, | |
| history: List[Dict[str, str]], | |
| ) -> Dict[str, Any]: | |
| """ | |
| Detect whether the user is asking about other available characters. | |
| Pure LLM classification β no keyword matching. | |
| """ | |
| _NULL = {"is_character_inquiry": False, "inquiry_type": None, "target_character": None} | |
| # Skip very short messages β they are almost never character inquiries | |
| if len(user_msg.split()) <= 3: | |
| return _NULL | |
| system = ( | |
| "The user is talking with a philosophical chatbot that has multiple characters " | |
| "(Socrates, Diogenes, Nietzsche, Camus, Schopenhauer). " | |
| "Detect whether the user's message is asking about other available characters " | |
| "or requesting a switch to a different one.\n\n" | |
| "Return ONLY valid JSON:\n" | |
| '{ "is_character_inquiry": true/false, "inquiry_type": "style" | "pragmatic" | "general" | null, ' | |
| '"target_character": "socrates" | "diogenes" | "nietzsche" | "camus" | "schopenhauer" | null }\n\n' | |
| "is_character_inquiry = true ONLY when the user:\n" | |
| " - Explicitly asks if there is SOMEONE ELSE or ANOTHER CHARACTER available\n" | |
| " - Asks for a character with a different personality or style\n" | |
| " - Asks who else they could speak to\n" | |
| " - Requests to switch to a different character\n\n" | |
| "is_character_inquiry = false when the user:\n" | |
| " - Is asking the CURRENT character to explain something better or in more detail\n" | |
| " - Is asking a follow-up question on the current topic\n" | |
| " - Is asking for step-by-step guidance from the current character\n" | |
| " - Uses phrases like 'walk me through', 'explain more', 'be more detailed'\n" | |
| " (these are requests TO the current character, not requests FOR a new one)\n\n" | |
| "inquiry_type (only when is_character_inquiry = true):\n" | |
| " style β user wants a different conversational style (more direct, gentler, etc.)\n" | |
| " pragmatic β user wants someone better at structured, step-by-step guidance\n" | |
| " general β user just wants to know who else is available\n" | |
| " null β not a character inquiry\n\n" | |
| "target_character = the specific character the user wants to be CONNECTED TO / TALK TO / " | |
| "SWITCHED TO by name (e.g. 'can you call Nietzsche?', 'let me talk to Camus', " | |
| "'bring Diogenes', 'pass me to him' referring to a named character). " | |
| "Set it ONLY when the user wants to actually switch to or speak with that specific person. " | |
| "Set it to null if the user merely asks what another character THINKS about the topic " | |
| "(that is a comparison question, not a switch), or names no one specific. " | |
| "When target_character is set, is_character_inquiry must also be true.\n\n" | |
| "When in doubt, return false β do not fire on ambiguous phrasing." | |
| ) | |
| recent = history[-4:] if history else [] | |
| msgs = [{"role": "system", "content": system}] | |
| for h in recent: | |
| role = h.get("role", "user") | |
| if role not in ("user", "assistant"): | |
| role = "user" | |
| msgs.append({"role": role, "content": h.get("content", "")}) | |
| msgs.append({"role": "user", "content": user_msg}) | |
| try: | |
| resp = client.chat.completions.create( | |
| model=OPENAI_CLASSIFIER_MODEL, | |
| messages=msgs, | |
| temperature=0.1, | |
| ) | |
| raw = (resp.choices[0].message.content or "").strip() | |
| parsed = _parse_json(raw, _NULL) | |
| _KNOWN = {"socrates", "diogenes", "nietzsche", "camus", "schopenhauer"} | |
| _target = (parsed.get("target_character") or "").lower().strip() | |
| if _target not in _KNOWN: | |
| _target = None | |
| return { | |
| "is_character_inquiry": bool(parsed.get("is_character_inquiry", False)) or bool(_target), | |
| "inquiry_type": parsed.get("inquiry_type") or None, | |
| "target_character": _target, | |
| } | |
| except Exception: | |
| return _NULL | |