Debdeep30 commited on
Commit
85d6f96
·
verified ·
1 Parent(s): 70dcc9c

Upload pipeline/parser.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. pipeline/parser.py +71 -0
pipeline/parser.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Structured output parser for Lumi.
3
+
4
+ Qwen3 outputs in its native thinking-first format:
5
+ <think>
6
+ Internal reasoning — NEVER sent to TTS or shown to user.
7
+ </think>
8
+ [avatar_tag] Short opening line.
9
+ Full warm companion response continues here.
10
+
11
+ TTS latency trick: stream until </think> appears, then fire TTS immediately
12
+ on the opening line that follows.
13
+
14
+ parse_structured_output() returns a dict with:
15
+ - avatar_tag : one of the 5 states (defaults to "smile" if missing)
16
+ - opening_line : fires TTS after </think> is seen (latency-hiding trick)
17
+ - full_response : complete response text (think block stripped)
18
+ """
19
+
20
+ import re
21
+
22
+ VALID_TAGS = {"smile", "nod", "concerned", "gentle", "laugh"}
23
+ _TAG_RE = re.compile(r"\[(\w+)\]") # anywhere in text
24
+ _TAG_START = re.compile(r"^\[(\w+)\]") # at start of text
25
+ _THINK_RE = re.compile(r"<think>.*?</think>", re.DOTALL)
26
+
27
+
28
+ def parse_structured_output(text: str) -> dict:
29
+ text = text.strip()
30
+
31
+ # Strip think block first — works for both tag-first and think-first formats
32
+ clean = _THINK_RE.sub("", text).strip()
33
+
34
+ # --- avatar tag: prefer at start of clean text, fall back to anywhere ----
35
+ tag_match = _TAG_START.match(clean) or _TAG_RE.search(clean)
36
+ if tag_match and tag_match.group(1).lower() in VALID_TAGS:
37
+ avatar_tag = tag_match.group(1).lower()
38
+ else:
39
+ avatar_tag = "smile"
40
+
41
+ # --- opening line: first line of the clean text after removing the tag ---
42
+ tag_bracket = f"[{avatar_tag}]"
43
+ body = clean.replace(tag_bracket, "").replace(tag_bracket.upper(), "").strip()
44
+ lines = [l.strip() for l in body.split("\n") if l.strip()]
45
+ opening_line = lines[0] if lines else body[:80]
46
+ full_response = body
47
+
48
+ return {
49
+ "avatar_tag": avatar_tag,
50
+ "opening_line": opening_line, # fire TTS after </think> token
51
+ "full_response": full_response, # append to TTS queue
52
+ }
53
+
54
+
55
+ def extract_facts_from_response(text: str) -> list[str]:
56
+ """
57
+ Heuristically extract memorable facts from a model response.
58
+ Used to build the patient's persistent memory in ChromaDB.
59
+ """
60
+ facts = []
61
+ lines = text.split(".")
62
+ for line in lines:
63
+ line = line.strip()
64
+ # keep lines that mention personal details, preferences, family
65
+ keywords = ("granddaughter", "grandson", "daughter", "son", "wife",
66
+ "husband", "likes", "loves", "favourite", "favorite",
67
+ "anniversary", "birthday", "garden", "music", "hymn",
68
+ "lonely", "feels", "misses", "remembers")
69
+ if any(k in line.lower() for k in keywords) and len(line) > 15:
70
+ facts.append(line)
71
+ return facts[:5] # cap at 5 per turn