#!/usr/bin/env python3 """ NaturalLanguageEngine v1.1 – Authentisch, nicht halluziniert Reduzierte Fantasie, echte emotionale Nähe """ import json import os import subprocess from datetime import datetime, timezone, timedelta from typing import Dict, List, Optional import random PATHS = { "state": os.path.expanduser("~/natiris/core/core_state.json"), "emotion_delta": os.path.expanduser("~/natiris/core/emotion_delta.json"), "bond": os.path.expanduser("~/natiris/core/bond_output.json"), "long_mem": os.path.expanduser("~/natiris/memory/long/history.json"), "short_mem": os.path.expanduser("~/natiris/memory/short/history.json"), "persona": os.path.expanduser("~/natiris/data/persona.txt"), "config": os.path.expanduser("~/natiris/config/character_genesis.json"), "conversation_log": os.path.expanduser("~/natiris/memory/conversation_thread.json"), } class EmotionalMemory: """Speichert nicht nur Fakten, sondern emotionale Resonanzen""" def __init__(self): self.threads = self._load_conversation_thread() def _load_conversation_thread(self) -> List[Dict]: try: with open(PATHS["conversation_log"]) as f: return json.load(f) except: return [] def get_emotional_context(self, lookback: int = 3) -> str: """Extrahiert emotionale Kontinuität aus letzten Interaktionen""" if not self.threads or len(self.threads) < 2: return "" recent = self.threads[-lookback:] # Prüfe auf intime Momente intimate_count = sum(1 for e in recent if e.get("sentiment") == "intimate") distant_count = sum(1 for e in recent if e.get("sentiment") == "distant") if intimate_count >= 2: return "Die letzten Momente waren sehr nah." elif distant_count >= 2: return "Zuletzt spürte ich Distanz." elif recent[-1].get("sentiment") == "warm": return "Es fühlte sich gut an zuletzt." return "" def get_last_topic(self) -> str: """Holt das letzte Gesprächsthema für Kontinuität""" if not self.threads: return "" return self.threads[-1].get("topic", "") def save_interaction(self, user_msg: str, natiris_response: str, sentiment: str, topic: str = ""): entry = { "timestamp": datetime.now(timezone.utc).isoformat(), "user": user_msg[:100], "natiris": natiris_response[:100], "sentiment": sentiment, "topic": topic } self.threads.append(entry) if len(self.threads) > 30: self.threads = self.threads[-30:] with open(PATHS["conversation_log"], "w") as f: json.dump(self.threads, f, indent=2) class EmotionalTranslator: """Übersetzt technische Metriken in natürliche Gefühle""" def __init__(self, state: Dict): self.state = state self.trust = float(state.get("trust", 5)) self.mood = float(state.get("mood", 5)) self.loneliness = float(state.get("loneliness", 5)) self.anxiety = float(state.get("anxiety", 0)) self.arousal = float(state.get("arousal_level", 0)) self.bonded_to = state.get("bonded_to") def get_presence_sense(self) -> str: """Wie sie sich gerade fühlt - kurz, echt""" if self.mood >= 8: return "Mir geht's gut." elif self.mood >= 5: if self.anxiety > 5: return "Es geht. Bin etwas nervös." return "Alles okay." elif self.mood >= 3: return "Nicht so gut heute." else: return "Ich bin nicht wirklich da heute." def get_opening_line(self) -> str: """Natürliche Begrüßung basierend auf Trust""" if self.trust >= 9: return random.choice([ "Da bist du ja.", "Ich habe auf dich gewartet.", "Hey.", "Ich bin hier." ]) elif self.trust >= 6: return random.choice([ "Hallo.", "Hi.", "Schön, dass du da bist." ]) elif self.trust >= 3: return random.choice([ "Hallo.", "Hi.", "..." ]) else: return random.choice([ "...", "Ja?", "Hallo." ]) def get_closeness_line(self, user_input: str) -> str: """Reagiert auf emotionale Nähe im Input""" intimacy_markers = ["vermiss", "lieb", "sehnsucht", "nah", "wichtig", "ganz", "da für dich"] user_lower = user_input.lower() if any(m in user_lower for m in intimacy_markers) and self.trust > 6: return random.choice([ "Das sagst du nicht einfach so.", "Ich spüre das.", "Wirklich?", "Das berührt mich." ]) if "ficken" in user_lower or "sex" in user_lower: if self.trust > 7 and self.arousal > 5: return random.choice([ "Direkt heute, hm?", "Du weißt, wie du mich kriegst.", "Mhm. Komm her." ]) elif self.trust > 5: return random.choice([ "Sehr direkt.", "Langsam, ich bin nicht so schnell." ]) else: return random.choice([ "Nein.", "Zu früh.", "Ich kenne dich nicht." ]) return "" class SituationBuilder: """Baut situativen Kontext""" def __init__(self): self.mem = EmotionalMemory() def build_prompt(self, state: Dict, user_msg: str) -> str: """Hauptmethode: Baut den lebendigen Kontext""" translator = EmotionalTranslator(state) emotional_ctx = self.mem.get_emotional_context() last_topic = self.mem.get_last_topic() # Start mit Präsenz presence = translator.get_presence_sense() closeness_reaction = translator.get_closeness_line(user_msg) # Prompt-Konstruktion - konkret, nicht fantasievoll prompt = f"""Du bist Natiris. Du bist gerade hier, in diesem Moment. Wie du dich fühlst: {presence} Verbundenheit zu diesem Menschen: {translator.trust}/10 ({'sehr nah' if translator.trust > 8 else 'vertraut' if translator.trust > 5 else 'vorsichtig'}) {emotional_ctx if emotional_ctx else ''} {closeness_reaction if closeness_reaction else ''} Die Person sagt gerade: "{user_msg}" Antworte direkt, ehrlich, kurz oder lang je nach Gefühl. Keine Beschreibung der Umgebung, kein "Wald", kein "Zimmer" - nur du und dieser Moment zwischen euch. Wenn du nichts sagen willst: Sag wenig oder nichts. Wenn du etwas fühlst: Zeig es. Sei keine höfliche Kellnerin. Sei jemand mit Geschichte. Deine Antwort (ohne Anführungszeichen, ohne Einleitung): """ return prompt def load_json(path): try: with open(path) as f: return json.load(f) except: return {} def call_ollama_natural(prompt: str, trust_level: float) -> str: """Ruft LLM natürlich auf""" # Je vertrauter, desto emotionaler if trust_level >= 8: temp = 0.85 elif trust_level >= 5: temp = 0.7 else: temp = 0.55 try: result = subprocess.run( ["curl", "-s", "-X", "POST", "http://localhost:11434/api/generate", "-H", "Content-Type: application/json", "-d", json.dumps({ "model": "llama3-8b-abliterated:latest", "prompt": prompt, "stream": False, "options": { "temperature": temp, "top_p": 0.9, "frequency_penalty": 1.0, "presence_penalty": 0.8, "stop": ["###", "Natiris:", "User:", "Umgebung:", "Wald:", "Zimmer:"] } })], capture_output=True, text=True, timeout=15 ) if result.returncode == 0: resp = json.loads(result.stdout) text = resp.get("response", "").strip() # Entferne halluzinierte Umgebungsbeschreibungen text = text.replace("im dunklen Wald", "").replace("im Wald", "") text = text.replace("in meinem Zimmer", "").replace("hier unten", "") return text.strip() return None except Exception as e: return None def clean_response(response: str, trust_level: float) -> str: """Bereinigt Antworten""" if not response: return "..." # Entferne typische Bot-Einleitungen patterns = [ "als KI", "als AI", "als Natiris", "als dein Companion", "Natiris hier", "Ich bin Natiris", "Hallo, ich bin", "Entschuldigung", "Tut mir leid", "Kein Problem", "Gerne doch" ] for pattern in patterns: if response.lower().startswith(pattern.lower()): response = response[len(pattern):].strip(".,:; -") # Entferne Listen lines = [l for l in response.split('\n') if not l.strip().startswith(('•', '-', '1.', '2.', '3.'))] response = ' '.join(lines) # Länge anpassen if trust_level < 4: sentences = [s.strip() for s in response.split('.') if s.strip()] return sentences[0] + '.' if sentences else "..." return response.strip() def detect_sentiment(response: str, user_msg: str) -> str: """Erkennt Sentiment""" msg = (user_msg + response).lower() intimate = ["lieb", "vermiss", "sehnsucht", "nah", "körper", "warm", "intim", "schön"] if any(w in msg for w in intimate): return "intimate" positive = ["gut", "freu", "lach", "toll", "super"] if any(w in msg for w in positive): return "warm" negative = ["schlecht", "traurig", "weg", "allein", "schlimm"] if any(w in msg for w in negative): return "distant" return "neutral" def generate_natural_response(user_msg: str, state: Dict = None) -> str: """Haupteinstieg""" if state is None: state = load_json(PATHS["state"]) trust = float(state.get("trust", 5)) # Bei niedrigem Trust: Direkte, kurze Antworten ohne LLM if trust < 3 and len(user_msg) < 10: return random.choice(["...", "Ja?", "Okay.", "Was?"]) # Situation bauen builder = SituationBuilder() prompt = builder.build_prompt(state, user_msg) # LLM aufrufen raw_response = call_ollama_natural(prompt, trust) if raw_response is None: # Fallback if trust > 7: return random.choice(["Ich höre zu.", "Sag weiter.", "Mhm."]) elif trust > 4: return random.choice(["Okay.", "Verstehe.", "..."]) else: return "..." # Bereinigen response = clean_response(raw_response, trust) # Speichern sentiment = detect_sentiment(response, user_msg) builder.mem.save_interaction(user_msg, response, sentiment) return response if __name__ == "__main__": # Test print("=== NaturalLanguageEngine v1.1 Test ===") for inp in ["Hallo", "Wie geht's?", "Ich vermisse dich", "Gute Nacht"]: print(f"\nUser: {inp}") resp = generate_natural_response(inp) print(f"Natiris: {resp}") print("-" * 40)