Spaces:
Running
Running
| """ | |
| modules/conversation.py β FRIDAY Continuous Conversation Engine | |
| REMOVES THE WAKE WORD BARRIER: | |
| - Free-talk mode - no "hey FRIDAY" needed | |
| - Context carries across turns | |
| - Intent detection, not just commands | |
| - Seamless multi-turn conversations | |
| """ | |
| import time | |
| import json | |
| import os | |
| from config import DATA_DIR | |
| CONV_FILE = os.path.join(DATA_DIR, "conversation.json") | |
| def _load() -> dict: | |
| if not os.path.exists(CONV_FILE): | |
| return {"mode": "command", "context": {}, "last_intent": "", "free_talk": False} | |
| try: | |
| with open(CONV_FILE, "r") as f: | |
| return json.load(f) | |
| except Exception: | |
| return {"mode": "command", "context": {}, "last_intent": "", "free_talk": False} | |
| def _save(data: dict): | |
| os.makedirs(DATA_DIR, exist_ok=True) | |
| try: | |
| with open(CONV_FILE, "w") as f: | |
| json.dump(data, f, indent=2) | |
| except Exception: | |
| pass | |
| # ββ Mode Control ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def enable_free_talk(enabled: bool = True): | |
| """Enable/disable free-talk mode.""" | |
| data = _load() | |
| data["free_talk"] = enabled | |
| data["mode"] = "free_talk" if enabled else "command" | |
| _save(data) | |
| def is_free_talk() -> bool: | |
| """Check if free-talk is enabled.""" | |
| return _load().get("free_talk", False) | |
| def get_mode() -> str: | |
| """Get current conversation mode.""" | |
| return _load().get("mode", "command") | |
| # ββ Intent Detection ββββββββββββββββββββββββββββββββββββββββββββ | |
| def detect_intent(text: str) -> str: | |
| """Detect intent from text.""" | |
| t = (text or "").lower() | |
| # Question intents | |
| if any(w in t for w in ["what", "how", "why", "when", "where", "which", "?"]): | |
| return "question" | |
| # Command intents | |
| if any(t.startswith(w) for w in ["open", "close", "start", "stop", "play", "pause", "set", "turn"]): | |
| return "command" | |
| # Request intents | |
| if any(w in t for w in ["can you", "please", "would you", "could", "help me"]): | |
| return "request" | |
| # Statement intents | |
| if any(w in t for w in ["i am", "i'm", "feeling", "working on", "doing"]): | |
| return "statement" | |
| # Opinion intents | |
| if any(w in t for w in ["think", "believe", "opinion", "should i"]): | |
| return "opinion" | |
| # Greeting intents | |
| if any(w in t for w in ["hey", "hi", "hello", "yo", "bro"]): | |
| return "greeting" | |
| # Emotional intents | |
| if any(w in t for w in ["frustrated", "annoyed", "happy", "excited", "sad", "tired"]): | |
| return "emotion" | |
| return "statement" | |
| # ββ Context Management ββββββββββββββββββββββββββββββββββββββββββββ | |
| def set_context(key: str, value): | |
| """Set conversation context.""" | |
| data = _load() | |
| data.setdefault("context", {})[key] = value | |
| _save(data) | |
| def get_context(key: str): | |
| """Get conversation context.""" | |
| return _load().get("context", {}).get(key) | |
| def clear_context(): | |
| """Clear conversation context.""" | |
| data = _load() | |
| data["context"] = {} | |
| _save(data) | |
| # ββ Multi-turn Continuation βββββββββββββββββββββββββββββββββββββββββ | |
| def is_follow_up(text: str) -> bool: | |
| """Check if this is a follow-up to previous turn.""" | |
| t = (text or "").lower() | |
| # Follow-up words | |
| follow_ups = ["that", "it", "them", "this", "also", "and", "but", "again", "more", "yes", "no", "ok", "sure"] | |
| # Pronouns that need context | |
| pronouns = ["it", "that", "this", "them", "they", "he", "she", "you", "we"] | |
| # Check for follow-up patterns | |
| first_word = t.split()[0] if t.split() else "" | |
| if first_word in follow_ups: | |
| return True | |
| # Short responses that need context | |
| if len(t.split()) <= 3 and first_word in pronouns: | |
| return True | |
| return False | |
| def get_follow_up_context() -> dict: | |
| """Get context needed for follow-up.""" | |
| data = _load() | |
| return data.get("context", {}) | |
| # ββ Should Respond βββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def should_respond(text: str) -> bool: | |
| """Decide if FRIDAY should respond without wake word.""" | |
| # Check free-talk mode | |
| if not is_free_talk(): | |
| return False | |
| t = (text or "").strip() | |
| if not t: | |
| return False | |
| # Check for name mentions | |
| if "friday" in t.lower() or "jarvis" in t.lower(): | |
| return True | |
| # Check for direct address | |
| if any(t.lower().startswith(w) for w in ["hey", "hi", "yo", "bro", "ok"]): | |
| return True | |
| # Follow-ups always respond | |
| if is_follow_up(t): | |
| return True | |
| # Check for question | |
| if "?" in t: | |
| return True | |
| # Short commands without wake word | |
| if detect_intent(t) == "command" and len(t.split()) <= 4: | |
| return True | |
| return False | |
| # ββ Natural Response Generation ββββββββββββββββββββββββββββββββββββββββ | |
| def get_natural_response(intent: str, context: dict) -> str: | |
| """Generate natural response based on intent.""" | |
| responses = { | |
| "question": [ | |
| "Good question. Let me think.", | |
| "Here's what I know:", | |
| "Let me look into that.", | |
| ], | |
| "command": [ | |
| "On it.", | |
| "Done.", | |
| "Consider it done.", | |
| ], | |
| "request": [ | |
| "Got it.", | |
| "Sure thing.", | |
| "I'll handle it.", | |
| ], | |
| "statement": [ | |
| "Interesting.", | |
| "Got it.", | |
| "Noted.", | |
| ], | |
| "emotion": [ | |
| "I hear you.", | |
| "Got it.", | |
| "I'm here.", | |
| ], | |
| } | |
| intents = responses.get(intent, responses["statement"]) | |
| import random | |
| return random.choice(intents) | |
| # ββ Brain Integration βββββββββββββββββββββββββββββββββββββββββ | |
| def prepare_for_brain(text: str) -> dict: | |
| """Prepare conversation context for brain.""" | |
| intent = detect_intent(text) | |
| data = _load() | |
| # Update context | |
| data["last_intent"] = intent | |
| data["last_text"] = text | |
| data["last_time"] = time.time() | |
| _save(data) | |
| return { | |
| "mode": get_mode(), | |
| "intent": intent, | |
| "context": get_follow_up_context(), | |
| "is_follow_up": is_follow_up(text), | |
| "should_respond": should_respond(text), | |
| } | |
| # ββ Toggle via Command ββββββββββββββββββββββββββββββββββββββββββ | |
| def toggle_free_talk_command(enable: bool = None) -> str: | |
| """Toggle free-talk mode via voice command.""" | |
| if enable is None: | |
| enable = not is_free_talk() | |
| enable_free_talk(enable) | |
| if enable: | |
| return "Free talk enabled. Just talk to me." | |
| else: | |
| return "Free talk disabled. Say my name to get my attention." |