Coverage for bot/byte_semantics_base.py: 86%
81 statements
« prev ^ index » next coverage.py v7.10.7, created at 2026-02-25 12:30 -0300
« prev ^ index » next coverage.py v7.10.7, created at 2026-02-25 12:30 -0300
1import re
3from bot.byte_semantics_constants import (
4 BYTE_INTRO_TEMPLATES,
5 BYTE_TRIGGER,
6 BYTE_TRIGGER_PATTERN,
7 COMPLEX_TECH_HINT_TERMS,
8 CURRENT_EVENTS_HINT_TERMS,
9 FOLLOW_UP_HINT_TERMS,
10 HIGH_RISK_CURRENT_EVENTS_TERMS,
11 MAX_CHAT_MESSAGE_LENGTH,
12 MOVIE_FACT_SHEET_PATTERN,
13 MULTIPART_SEPARATOR,
14 RELEVANCE_HINT_TERMS,
15 SERIOUS_TECH_TERMS,
16)
17from bot.logic_context import enforce_reply_limits
19_intro_template_index = 0
22def compact_message(text: str, max_len: int = 450) -> str:
23 if len(text) <= max_len:
24 return text
25 head = text[: max_len - 3].rstrip()
26 punctuation_positions = [head.rfind(symbol) for symbol in (".", "!", "?", ";", ":")]
27 best_punctuation = max(punctuation_positions)
28 if best_punctuation >= int(max_len * 0.5):
29 return head[: best_punctuation + 1].strip()
30 last_space = head.rfind(" ")
31 if last_space >= int(max_len * 0.5):
32 head = head[:last_space]
33 return head.rstrip(" ,;:") + "..."
36def normalize_text_for_scene(text: str, max_len: int = 120) -> str:
37 compact = " ".join((text or "").split())
38 if len(compact) <= max_len:
39 return compact
40 return compact[: max_len - 3].rstrip() + "..."
43def format_chat_reply(text: str) -> str:
44 normalized = str(text or "").replace(MULTIPART_SEPARATOR, "\n")
45 return compact_message(
46 enforce_reply_limits(normalized),
47 max_len=MAX_CHAT_MESSAGE_LENGTH,
48 )
51def parse_byte_prompt(message_text: str) -> str | None:
52 text = (message_text or "").strip()
53 if not text:
54 return None
55 normalized_text = text.lower()
56 if normalized_text in {BYTE_TRIGGER, f"!{BYTE_TRIGGER}", f"@{BYTE_TRIGGER}"}:
57 return ""
58 match = BYTE_TRIGGER_PATTERN.match(text)
59 if not match:
60 return None
61 return match.group(1).strip()
64def is_movie_fact_sheet_prompt(prompt: str) -> bool:
65 lowered = (prompt or "").lower()
66 return bool(MOVIE_FACT_SHEET_PATTERN.search(lowered))
69def is_intro_prompt(prompt: str) -> bool:
70 normalized = " ".join((prompt or "").lower().split()).strip(" ?!.,:")
71 if not normalized:
72 return False
73 exact_triggers = {
74 "se apresente",
75 "apresente se",
76 "apresente-se",
77 "quem e voce",
78 "quem e vc",
79 "quem e o byte",
80 "o que voce faz",
81 }
82 if normalized in exact_triggers:
83 return True
84 return normalized.startswith("se apresente")
87def build_intro_reply() -> str:
88 global _intro_template_index
89 template = BYTE_INTRO_TEMPLATES[_intro_template_index % len(BYTE_INTRO_TEMPLATES)]
90 _intro_template_index += 1
91 return template
94def is_current_events_prompt(prompt: str) -> bool:
95 lowered = (prompt or "").lower()
96 return any(hint in lowered for hint in CURRENT_EVENTS_HINT_TERMS)
99def is_high_risk_current_events_prompt(prompt: str) -> bool:
100 lowered = (prompt or "").lower()
101 return any(hint in lowered for hint in HIGH_RISK_CURRENT_EVENTS_TERMS)
104def is_serious_technical_prompt(prompt: str) -> bool:
105 normalized = " ".join((prompt or "").lower().split()).strip(" ?!.,:")
106 if len(normalized) < 24:
107 return False
108 has_technical_signal = any(term in normalized for term in SERIOUS_TECH_TERMS) or any(
109 term in normalized for term in COMPLEX_TECH_HINT_TERMS
110 )
111 if not has_technical_signal:
112 return False
113 has_relevance_signal = any(
114 term in normalized for term in RELEVANCE_HINT_TERMS
115 ) or is_current_events_prompt(normalized)
116 return has_relevance_signal
119def is_follow_up_prompt(prompt: str) -> bool:
120 normalized = " ".join((prompt or "").lower().split()).strip(" ?!.,:")
121 if not normalized:
122 return False
123 if normalized in {"e agora", "e ai", "e aí", "e esse", "e essa", "e ele", "e ela"}:
124 return True
125 for term in FOLLOW_UP_HINT_TERMS:
126 boundary_pattern = rf"(^|[^a-z0-9]){re.escape(term)}([^a-z0-9]|$)"
127 if re.search(boundary_pattern, normalized):
128 return True
129 if normalized.startswith(("e ", "entao ", "então ")) and len(normalized.split()) <= 7:
130 return True
131 words = normalized.split()
132 if len(words) <= 5 and any(word in {"isso", "ele", "ela", "esse", "essa"} for word in words):
133 return True
134 return False