File size: 7,571 Bytes
a31f556
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
"""

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