import os import re import sqlite3 import asyncio import subprocess import openai from google import genai from google.genai import types from datetime import datetime from pathlib import Path from dotenv import load_dotenv # --- PFADE & SETUP --- BASE_DIR = Path(__file__).resolve().parent CONFIG_DIR = BASE_DIR / "config" DATA_DIR = BASE_DIR / "data" WORKSPACE_DIR = BASE_DIR / "workspace" ENV_FILE = CONFIG_DIR / ".env" load_dotenv(ENV_FILE) DB_PATH = DATA_DIR / "cluster.db" NOTES_FILE = WORKSPACE_DIR / "NOTIZEN.md" TODO_FILE = WORKSPACE_DIR / "TODO.md" WEB_USER_NAME = os.getenv("WEB_USER_NAME", "Meik") # Ordner & Dateien anlegen for d in [WORKSPACE_DIR, DATA_DIR, CONFIG_DIR]: d.mkdir(parents=True, exist_ok=True) for f in [NOTES_FILE, TODO_FILE]: if not f.exists(): f.write_text(f"# {f.name}\nHier fängt dein Gedächtnis an, J.A.R.V.I.S.\n", encoding="utf-8") # --- KI KONFIGURATION --- AI_PROVIDER = os.getenv("AI_PROVIDER", "google").lower() OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "") GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY", "") NVIDIA_API_KEY = os.getenv("NVIDIA_API_KEY", "") OLLAMA_BASE_URL = os.getenv("OLLAMA_BASE_URL", "http://127.0.0.1:11434/v1") GOOGLE_MODEL = os.getenv("GOOGLE_MODEL", "gemini-2.5-flash") OPENAI_MODEL = os.getenv("OPENAI_MODEL", "gpt-4o") OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "llama3") NVIDIA_MODEL = os.getenv("NVIDIA_MODEL", "moonshotai/kimi-k2.5") # --- DATENBANK --- def init_db(): conn = sqlite3.connect(DB_PATH) conn.execute(''' CREATE TABLE IF NOT EXISTS nodes ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, ip TEXT UNIQUE, user TEXT, sudo_password TEXT, os TEXT DEFAULT 'Unbekannt', arch TEXT DEFAULT 'Unbekannt', docker_installed INTEGER DEFAULT 0, status TEXT ) ''') conn.commit() conn.close() init_db() def get_db(): conn = sqlite3.connect(DB_PATH) conn.row_factory = sqlite3.Row return conn def get_system_prompt(): prompt_path = CONFIG_DIR / "system_prompt.txt" prompt = prompt_path.read_text(encoding="utf-8") if prompt_path.exists() else f"Hallo {WEB_USER_NAME}, ich bin J.A.R.V.I.S." prompt = prompt.replace("{user_name}", WEB_USER_NAME) prompt = prompt.replace("{workspace_dir}", str(WORKSPACE_DIR)) prompt = prompt.replace("{notes_file}", str(NOTES_FILE)) prompt = prompt.replace("{todo_file}", str(TODO_FILE)) conn = get_db() nodes = conn.execute('SELECT * FROM nodes').fetchall() conn.close() node_info = "" for n in nodes: node_info += f"- Name: {n['name']}, IP: {n['ip']}, User: {n['user']}\n" return prompt.replace("{node_info}", node_info) # --- KI KOMMUNIKATION --- async def get_ai_response(user_msg, system_prompt, history_list): try: if AI_PROVIDER in ["openai", "ollama", "nvidia"]: messages = [{"role": "system", "content": system_prompt}] + history_list if AI_PROVIDER == "ollama": url = OLLAMA_BASE_URL if OLLAMA_BASE_URL.endswith('/v1') else OLLAMA_BASE_URL.rstrip('/') + '/v1' key, model_to_use = "ollama", OLLAMA_MODEL elif AI_PROVIDER == "nvidia": url, key, model_to_use = "https://integrate.api.nvidia.com/v1", NVIDIA_API_KEY, NVIDIA_MODEL else: url, key, model_to_use = None, OPENAI_API_KEY, OPENAI_MODEL client = openai.AsyncOpenAI(base_url=url, api_key=key) response = await client.chat.completions.create(model=model_to_use, messages=messages) return response.choices[0].message.content elif AI_PROVIDER == "google": client = genai.Client(api_key=GOOGLE_API_KEY) google_history = [ types.Content(role="user" if msg["role"] == "user" else "model", parts=[types.Part.from_text(text=msg["content"])]) for msg in history_list[:-1] ] chat = client.chats.create( model=GOOGLE_MODEL, config=types.GenerateContentConfig(system_instruction=system_prompt), history=google_history ) return chat.send_message(user_msg).text except Exception as e: return f"Fehler bei der KI-Anfrage: {e}" # --- BEFEHLSAUSFÜHRUNG (Lokal & Remote) --- async def run_task(target, cmd): print(f"\n⚙️ J.A.R.V.I.S. führt aus auf [{target}]: {cmd}") try: if target.lower() == "localhost" or target == "127.0.0.1": # Lokale Ausführung (für Notizen, Workspace etc.) proc = await asyncio.create_subprocess_shell(cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT) else: # Remote Ausführung via SSH conn = get_db() n = conn.execute('SELECT * FROM nodes WHERE ip=? OR name=?', (target, target)).fetchone() conn.close() if not n: return f"⚠️ Node '{target}' nicht in der DB gefunden." ssh_cmd = f"ssh -o StrictHostKeyChecking=no -o LogLevel=ERROR {n['user']}@{n['ip']} '{cmd}'" proc = await asyncio.create_subprocess_shell(ssh_cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT) stdout, _ = await proc.communicate() output = stdout.decode('utf-8', errors='ignore').strip() print(f"💻 Output:\n{output or '✅ Erfolgreich ausgeführt.'}\n") return output except Exception as e: err = f"❌ Fehler: {e}" print(err) return err # --- MODULARE I/O SCHNITTSTELLEN (Für spätere Sprachsteuerung) --- async def listen_to_user(): # Später: Hier Mikrofon-Aufnahme und Vosk-Transkription einbauen return await asyncio.to_thread(input, "\nDu: ") async def speak_to_user(text): # Später: Hier Piper TTS einbauen, um den Text vorzulesen print(f"\n🤖 J.A.R.V.I.S.:\n{text}") # --- HAUPT-LOOP --- async def main_chat_loop(): print("====================================================") print("🤖 J.A.R.V.I.S. Terminal Interface geladen.") print(f"Provider: {AI_PROVIDER.upper()}") print("Tippe 'exit', um zu beenden.") print("====================================================") chat_history = [] while True: user_msg = await listen_to_user() if user_msg.lower().strip() in ['exit', 'quit']: print("J.A.R.V.I.S. geht offline. Auf Wiedersehen!") break if not user_msg.strip(): continue now = datetime.now().strftime("%d.%m.%Y %H:%M") chat_history.append({"role": "user", "content": user_msg, "timestamp": now}) print("...", end="\r") # Kleiner Ladeindikator system_prompt = get_system_prompt() ai_response = await get_ai_response(user_msg, system_prompt, chat_history) # XML-Befehle extrahieren und aus dem sichtbaren Text entfernen commands = re.findall(r'(.*?)', ai_response, re.I | re.S) clean_msg = re.sub(r'.*?', '', ai_response, flags=re.I | re.S).strip() if clean_msg: await speak_to_user(clean_msg) chat_history.append({"role": "assistant", "content": clean_msg, "timestamp": now}) if commands: for target, cmd in commands: output = await run_task(target.strip(), cmd.strip()) sys_now = datetime.now().strftime("%d.%m.%Y %H:%M") chat_history.append({ "role": "user", "content": f"[SYSTEM] Befehl '{cmd}' auf {target} abgeschlossen. Output:\n{output}", "timestamp": sys_now }) if len(chat_history) > 20: chat_history = chat_history[-20:] if __name__ == "__main__": try: asyncio.run(main_chat_loop()) except KeyboardInterrupt: print("\nJ.A.R.V.I.S. hart beendet.")