| """ |
| Conversation Engine β handles general chat using pattern-response pairs. |
| |
| How it works: |
| 1. Loads 'conversation.txt' at startup. |
| Each non-comment line format: PATTERN|||RESPONSE |
| 2. Tries to match user input against compiled regex patterns. |
| 3. Returns (response, matched): |
| matched = True β a pattern fired; use the response directly |
| matched = False β no match; caller should escalate to LLM fallback |
| |
| EXTENSION POINT: The respond() method can be replaced wholesale with an LLM |
| call without touching any other module. Contract: (str) β (str, bool). |
| """ |
|
|
| import os |
| import re |
| from typing import List, Tuple |
|
|
|
|
| CONVERSATION_FILE = os.path.join(os.path.dirname(__file__), "conversation.txt") |
|
|
|
|
| def _load_patterns(filepath: str) -> List[Tuple[re.Pattern, str]]: |
| patterns: List[Tuple[re.Pattern, str]] = [] |
| if not os.path.exists(filepath): |
| return patterns |
| with open(filepath, "r", encoding="utf-8") as f: |
| for line in f: |
| line = line.strip() |
| if not line or line.startswith("#"): |
| continue |
| if "|||" not in line: |
| continue |
| pattern_part, response_part = line.split("|||", 1) |
| pattern_str = pattern_part.strip() |
| response_str = response_part.strip() |
| if not pattern_str or not response_str: |
| continue |
| try: |
| compiled = re.compile(pattern_str, re.IGNORECASE) |
| patterns.append((compiled, response_str)) |
| except re.error: |
| continue |
| return patterns |
|
|
|
|
| class ConversationEngine: |
| """Rule-based pattern-matching chat engine backed by conversation.txt.""" |
|
|
| def __init__(self, conversation_file: str = CONVERSATION_FILE): |
| self.patterns = _load_patterns(conversation_file) |
|
|
| def respond(self, user_input: str) -> Tuple[str, bool]: |
| """ |
| Match user input against stored patterns. |
| |
| Returns: |
| (response, matched) |
| matched = True β pattern found, response is ready to use |
| matched = False β no pattern matched; escalate to LLM |
| """ |
| text = user_input.strip() |
| for pattern, response in self.patterns: |
| if pattern.search(text): |
| return (response, True) |
| |
| return ("", False) |
|
|