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

1import re 

2 

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 

18 

19_intro_template_index = 0 

20 

21 

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(" ,;:") + "..." 

34 

35 

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() + "..." 

41 

42 

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 ) 

49 

50 

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() 

62 

63 

64def is_movie_fact_sheet_prompt(prompt: str) -> bool: 

65 lowered = (prompt or "").lower() 

66 return bool(MOVIE_FACT_SHEET_PATTERN.search(lowered)) 

67 

68 

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") 

85 

86 

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 

92 

93 

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) 

97 

98 

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) 

102 

103 

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 

117 

118 

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