jarvis-cloud / modules /conversation.py
Jarvis2345's picture
Squash history β€” remove all prior commits (secret hygiene, S4)
a31f556
Raw
History Blame Contribute Delete
7.57 kB
"""
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."