| from gevent import monkey |
|
|
| monkey.patch_all() |
|
|
| import json |
| import time |
| from pathlib import Path |
|
|
| import gevent |
| from flask import Flask, request, jsonify, render_template_string |
| from flask_socketio import SocketIO, emit |
| import threading |
| from infj_bot.core.cognitive_orchestrator import CognitiveOrchestrator |
|
|
| |
| try: |
| import sys as _sys |
|
|
| _hive_path = str(Path(__file__).resolve().parent / "hive_mind") |
| if _hive_path not in _sys.path: |
| _sys.path.insert(0, _hive_path) |
| from drift_bridge import DriftBridge as _DriftBridge |
|
|
| _obs_drift = _DriftBridge() |
| _OBSERVATORY_ENABLED = True |
| except Exception: |
| _OBSERVATORY_ENABLED = False |
|
|
| from infj_bot.core.brain import DriftBrain |
| from infj_bot.core.commands import BotState, handle_command |
| from infj_bot.core.plugins.growth import growth_profile |
| from infj_bot.core.history import ChatHistory |
| from infj_bot.core.memory import DriftMemory |
| from infj_bot.core.plugins.goals import GoalsDB |
| from infj_bot.core.config import DATA_DIR, DEFAULT_AUTHORIZED_TARGETS |
|
|
| STATE_ROOT = DATA_DIR |
| from infj_bot.core.plugins.documents import DocumentStore |
| from infj_bot.core.prompt_builder import build_chat_prompt |
|
|
|
|
| from infj_bot.core.plugins.preferences import PreferenceStore |
| from infj_bot.core.plugins.scheduler import TaskScheduler |
| from infj_bot.core.plugins.self_eval import SelfEvaluator |
| from infj_bot.core.gen_cache import DiskGenCache |
|
|
| SESSIONS_DIR = STATE_ROOT / "sessions" |
| SESSIONS_DIR.mkdir(parents=True, exist_ok=True) |
|
|
| sessions = {} |
| sessions_lock = threading.Lock() |
|
|
| class SessionResources: |
| def __init__(self, session_id: str): |
| self.session_id = session_id |
| if session_id == "global": |
| self.session_dir = STATE_ROOT / "global_session" |
| else: |
| self.session_dir = SESSIONS_DIR / session_id |
| self.session_dir.mkdir(parents=True, exist_ok=True) |
| |
| |
| self.state = BotState( |
| authorized_targets=set(DEFAULT_AUTHORIZED_TARGETS), |
| prefs=PreferenceStore(db_path=self.session_dir / "preferences.db"), |
| scheduler=TaskScheduler(db_path=self.session_dir / "scheduler.db") |
| ) |
| |
| |
| self.memory = DriftMemory( |
| persist_directory=str(self.session_dir / "memory") |
| ) |
| |
| |
| self.doc_store = DocumentStore( |
| persist_directory=str(self.session_dir / "chroma_db") |
| ) |
| |
| |
| self.goals_db = GoalsDB( |
| db_path=self.session_dir / "goals.db" |
| ) |
| |
| |
| self.history = ChatHistory( |
| path=self.session_dir / "history.jsonl" |
| ) |
| |
| |
| evaluator = SelfEvaluator(db_path=self.session_dir / "self_eval.db") |
| try: |
| disk_cache = DiskGenCache( |
| path=self.session_dir / "drift_gen_cache.sqlite3" |
| ) |
| except Exception: |
| disk_cache = None |
| self.brain = DriftBrain(evaluator=evaluator, disk_cache=disk_cache) |
| |
| self.last_accessed = time.time() |
| |
| def close(self): |
| |
| if hasattr(self.brain, "_disk_cache") and self.brain._disk_cache: |
| try: |
| self.brain._disk_cache.close() |
| except Exception: |
| pass |
|
|
|
|
| def get_session(session_id: str = None) -> SessionResources: |
| if not session_id: |
| session_id = "global" |
| with sessions_lock: |
| if session_id not in sessions: |
| sessions[session_id] = SessionResources(session_id) |
| else: |
| sessions[session_id].last_accessed = time.time() |
| return sessions[session_id] |
|
|
|
|
| def get_request_session_id(): |
| """Extracts session_id from JSON payload or cookie fallback.""" |
| session_id = None |
| if request.is_json: |
| try: |
| payload = request.json or {} |
| session_id = payload.get("session_id") |
| except Exception: |
| pass |
| if not session_id: |
| session_id = request.cookies.get("drift_session_id") |
| return session_id |
|
|
|
|
| def prune_and_cleanup_sessions(): |
| """Prunes inactive in-memory sessions and cleans up old disk folders.""" |
| while True: |
| try: |
| now = time.time() |
| |
| with sessions_lock: |
| inactive_keys = [] |
| for sid, sres in sessions.items(): |
| if sid == "global": |
| continue |
| if now - sres.last_accessed > 1800: |
| inactive_keys.append(sid) |
| for sid in inactive_keys: |
| print(f"Pruning in-memory session: {sid}") |
| sres = sessions.pop(sid) |
| sres.close() |
| |
| |
| if SESSIONS_DIR.exists(): |
| import shutil |
| for p in SESSIONS_DIR.iterdir(): |
| if p.is_dir(): |
| with sessions_lock: |
| is_loaded = p.name in sessions |
| if not is_loaded: |
| try: |
| mtime = p.stat().st_mtime |
| for filepath in p.rglob("*"): |
| if filepath.is_file(): |
| mtime = max(mtime, filepath.stat().st_mtime) |
| if now - mtime > 7 * 86400: |
| print(f"Deleting expired session folder on disk: {p.name}") |
| shutil.rmtree(p, ignore_errors=True) |
| except Exception: |
| pass |
| except Exception as e: |
| print(f"Error in pruning sessions: {e}") |
| gevent.sleep(300) |
|
|
|
|
| def background_cognitive_loop(): |
| """Background loop to tick homeostasis, shadow, and embodiment for the active sessions.""" |
| from infj_bot.core.being import get_being |
| from infj_bot.core.homeostasis import get_homeostasis |
| from infj_bot.core.shadow import get_shadow |
| from infj_bot.core.dii_tracker import get_dii_tracker |
| from infj_bot.core.global_workspace import get_workspace |
| |
| being = get_being() |
| homeostasis = get_homeostasis() |
| shadow = get_shadow() |
| tracker = get_dii_tracker() |
| workspace = get_workspace() |
| |
| while True: |
| try: |
| |
| embodiment_plugin = cognitive_orchestrator.arch.get_plugin("embodiment") |
| embodiment = embodiment_plugin.instance if embodiment_plugin else None |
| if embodiment: |
| import random |
| bpm = getattr(embodiment, "heartbeat_bpm", 72) |
| bpm += random.uniform(-1.5, 1.5) |
| bpm = max(60, min(100, bpm)) |
| setattr(embodiment, "heartbeat_bpm", bpm) |
| |
| phase = getattr(embodiment, "breath_phase", "exhale") |
| depth = getattr(embodiment, "breath_depth", 0.65) |
| depth += random.uniform(-0.05, 0.05) |
| depth = max(0.3, min(0.95, depth)) |
| setattr(embodiment, "breath_depth", depth) |
| |
| if random.random() < 0.2: |
| setattr(embodiment, "breath_phase", "inhale" if phase == "exhale" else "exhale") |
|
|
| |
| if hasattr(shadow, "background_tick"): |
| shadow.background_tick(being=being) |
| if hasattr(homeostasis, "background_cycle"): |
| homeostasis.background_cycle(being=being) |
| |
| |
| if hasattr(tracker, "compute"): |
| tracker.compute( |
| being=being, |
| workspace=workspace, |
| homeostasis=homeostasis, |
| shadow=shadow, |
| orchestrator=cognitive_orchestrator, |
| ) |
| except Exception as e: |
| print(f"Error in background cognitive cycle: {e}") |
| gevent.sleep(4.0) |
|
|
|
|
| _MAX_PAYLOAD = 1_048_576 |
|
|
|
|
| INDEX_HTML = """<!doctype html> |
| <html> |
| <head> |
| <meta charset="utf-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1"> |
| <title>DRIFT // DASHBOARD</title> |
| <style> |
| @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;600;700&family=Rajdhani:wght@400;500;600;700&display=swap'); |
| |
| :root { |
| --bg-deep: #050505; |
| --bg-surface: #111111; |
| --accent-orange: #FF5500; |
| --accent-white: #FFFFFF; |
| --text-muted: #A0A0A0; |
| --border-dim: #222; |
| } |
| |
| body { |
| margin: 0; |
| font-family: 'Rajdhani', sans-serif; |
| background: var(--bg-deep); |
| color: var(--accent-white); |
| overflow: hidden; |
| } |
| |
| main { |
| display: grid; |
| grid-template-columns: 1fr 340px; |
| height: 100vh; |
| overflow: hidden; |
| } |
| |
| section#chat { |
| display: flex; |
| flex-direction: column; |
| padding: 20px; |
| min-height: 0; |
| overflow: hidden; |
| background-image: radial-gradient(circle at 50% 50%, #0a0a0a 0%, #050505 100%); |
| } |
| |
| header { |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| margin-bottom: 20px; |
| border-bottom: 1px solid var(--border-dim); |
| padding-bottom: 10px; |
| } |
| |
| header h1 { |
| font-family: 'Orbitron', sans-serif; |
| font-size: 1.2rem; |
| letter-spacing: 2px; |
| margin: 0; |
| color: var(--accent-orange); |
| } |
| |
| #messages { |
| flex: 1; |
| overflow-y: auto; |
| display: flex; |
| flex-direction: column; |
| gap: 15px; |
| padding-right: 10px; |
| scrollbar-width: thin; |
| scrollbar-color: var(--accent-orange) var(--bg-deep); |
| } |
| |
| .msg { |
| padding: 12px 16px; |
| border-radius: 2px; |
| max-width: 85%; |
| line-height: 1.5; |
| position: relative; |
| } |
| |
| .user { |
| align-self: flex-end; |
| background: var(--bg-surface); |
| border: 1px solid #444; |
| color: var(--accent-white); |
| } |
| |
| .bot { |
| align-self: flex-start; |
| background: rgba(255, 85, 0, 0.05); |
| border: 1px solid var(--accent-orange); |
| color: var(--accent-white); |
| border-left-width: 4px; |
| } |
| |
| form#form { |
| display: flex; |
| gap: 10px; |
| margin-top: 20px; |
| background: var(--bg-surface); |
| padding: 5px; |
| border: 1px solid var(--border-dim); |
| } |
| |
| input#input { |
| flex: 1; |
| background: transparent; |
| border: none; |
| color: var(--accent-white); |
| padding: 12px; |
| font-family: 'Rajdhani', sans-serif; |
| font-size: 1rem; |
| } |
| |
| input#input:focus { outline: none; } |
| |
| button { |
| background: var(--accent-orange); |
| color: var(--bg-deep); |
| border: none; |
| padding: 0 25px; |
| font-family: 'Orbitron', sans-serif; |
| font-weight: 700; |
| cursor: pointer; |
| text-transform: uppercase; |
| font-size: 0.8rem; |
| transition: all 0.2s; |
| } |
| |
| button:hover { background: var(--accent-white); } |
| |
| aside { |
| background: var(--bg-surface); |
| border-left: 1px solid var(--border-dim); |
| padding: 20px; |
| min-height: 0; |
| overflow-y: auto; |
| scrollbar-width: none; |
| } |
| |
| .panel { |
| margin-bottom: 25px; |
| border: 1px solid var(--border-dim); |
| padding: 15px; |
| position: relative; |
| } |
| |
| .panel::after { |
| content: ''; |
| position: absolute; |
| top: 0; left: 0; width: 10px; height: 10px; |
| border-top: 2px solid var(--accent-orange); |
| border-left: 2px solid var(--accent-orange); |
| } |
| |
| .panel label { |
| font-family: 'Orbitron', sans-serif; |
| font-size: 0.7rem; |
| color: var(--accent-orange); |
| text-transform: uppercase; |
| letter-spacing: 1px; |
| display: block; |
| margin-bottom: 10px; |
| } |
| |
| select, textarea, input.side-input { |
| width: 100%; |
| background: #0a0a0a; |
| border: 1px solid var(--border-dim); |
| color: var(--accent-white); |
| padding: 8px; |
| font-family: 'Rajdhani', sans-serif; |
| margin-bottom: 10px; |
| box-sizing: border-box; |
| } |
| |
| #growthCard { |
| text-align: center; |
| } |
| |
| #growthAvatar { |
| height: 120px; |
| display: flex; |
| justify-content: center; |
| align-items: center; |
| background: #0a0a0a; |
| border: 1px solid var(--border-dim); |
| margin-bottom: 15px; |
| position: relative; |
| } |
| |
| #growthBar { |
| height: 4px; |
| background: #222; |
| margin: 10px 0; |
| position: relative; |
| } |
| |
| #growthFill { |
| height: 100%; |
| width: 0%; |
| background: var(--accent-orange); |
| box-shadow: 0 0 10px var(--accent-orange); |
| transition: width 0.5s ease; |
| } |
| |
| #growthStage { |
| font-family: 'Orbitron', sans-serif; |
| font-weight: 700; |
| color: var(--accent-white); |
| font-size: 0.9rem; |
| } |
| |
| .small { |
| color: var(--text-muted); |
| font-size: 0.8rem; |
| white-space: pre-wrap; |
| } |
| |
| .obs-link { |
| display: block; |
| text-align: center; |
| padding: 12px; |
| background: var(--accent-orange); |
| color: var(--bg-deep); |
| font-family: 'Orbitron', sans-serif; |
| font-weight: 700; |
| text-decoration: none; |
| font-size: 0.8rem; |
| letter-spacing: 1px; |
| transition: all 0.2s; |
| } |
| |
| .obs-link:hover { background: var(--accent-white); } |
| |
| /* Creature Styles (adapted for orange theme) */ |
| .creature { --scale: 1; position: relative; width: 64px; height: 72px; transform: scale(var(--scale)); } |
| .creature .body { position: absolute; left: 10px; top: 18px; width: 44px; height: 46px; border-radius: 45% 45% 38% 38%; background: var(--accent-orange); box-shadow: inset -8px -10px 0 #cc4400; } |
| .creature .eye { position: absolute; top: 34px; width: 6px; height: 8px; border-radius: 50%; background: #000; z-index: 2; } |
| .creature .eye.left { left: 24px; } |
| .creature .eye.right { right: 24px; } |
| .creature .mouth { position: absolute; left: 29px; top: 47px; width: 10px; height: 5px; border-bottom: 2px solid #000; border-radius: 0 0 12px 12px; z-index: 2; } |
| .creature .leaf { position: absolute; left: 28px; top: 3px; width: 10px; height: 24px; border-radius: 90% 10% 90% 10%; background: #fff; transform-origin: bottom center; transform: rotate(-22deg); opacity: 0; } |
| .creature .glow { position: absolute; left: 18px; top: 22px; width: 28px; height: 28px; border-radius: 50%; background: #fff; opacity: 0; filter: blur(6px); } |
| |
| @media (max-width: 768px) { |
| body { overflow: auto; } |
| main { |
| grid-template-columns: 1fr; |
| grid-template-rows: auto auto; |
| height: auto; |
| min-height: 100vh; |
| overflow: visible; |
| } |
| section#chat { |
| min-height: 60vh; |
| overflow: visible; |
| } |
| aside { |
| border-left: none; |
| border-top: 1px solid var(--border-dim); |
| max-height: none; |
| } |
| } |
| </style> |
| </head> |
| <body> |
| <main> |
| <section id="chat"> |
| <header> |
| <h1>DRIFT // NEURAL INTERFACE</h1> |
| <div class="small" id="connection-status">ONLINE // SESSION ACTIVE</div> |
| </header> |
| <div id="messages"></div> |
| <form id="form"> |
| <input id="input" autocomplete="off" placeholder="TRANSMIT MESSAGE..."> |
| <button>SEND</button> |
| </form> |
| </section> |
| <aside> |
| <div class="panel" id="growthCard"> |
| <label>Biological Growth</label> |
| <div id="growthAvatar"> |
| <div id="growthCreature" class="creature spark"> |
| <div class="glow"></div><div class="leaf"></div><div class="body"></div><div class="eye left"></div><div class="eye right"></div><div class="mouth"></div> |
| </div> |
| </div> |
| <div id="growthStage">INITIALIZING...</div> |
| <div id="growthBar"><div id="growthFill"></div></div> |
| <div id="growthDesc" class="small"></div> |
| <pre id="growthStats" class="small" style="text-align:left; margin-top:10px;"></pre> |
| </div> |
| |
| <div class="panel"> |
| <label>Cognitive Mode</label> |
| <select id="mode"> |
| <option>companion</option><option>engineer</option><option>critic</option> |
| <option>coach</option><option>clarity</option><option>researcher</option><option>bughunter</option><option>quiet</option> |
| </select> |
| <div style="display:flex; gap:5px;"> |
| <button id="status" style="flex:1; padding:8px 0;">STATUS</button> |
| <button id="reflect" style="flex:1; padding:8px 0;">REFLECT</button> |
| </div> |
| <pre id="side" class="small" style="margin-top:10px;"></pre> |
| </div> |
| |
| <div class="panel"> |
| <label>Memory Retrieval</label> |
| <textarea id="query" rows="2" placeholder="QUERY PARAMETERS..."></textarea> |
| <button id="search" style="width:100%; padding:8px 0;">SEARCH</button> |
| </div> |
| |
| <div class="panel" style="border:none; padding:0; display:flex; flex-direction:column; gap:8px;"> |
| <a href="/observatory" target="_blank" class="obs-link">OPEN OBSERVATORY</a> |
| <a href="/glyph" target="_blank" class="obs-link" style="background:#111; color:var(--accent-orange); border:1px solid var(--accent-orange);">GLYPH SYSTEM</a> |
| </div> |
| </aside> |
| </main> |
| <script> |
| const messages = document.querySelector('#messages'); |
| function add(cls, text) { |
| const div = document.createElement('div'); |
| div.className = 'msg ' + cls; |
| div.textContent = text; |
| messages.appendChild(div); |
| messages.scrollTop = messages.scrollHeight; |
| } |
| async function post(path, body={}) { |
| const res = await fetch(path, {method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify(body)}); |
| return await res.json(); |
| } |
| async function refreshGrowth() { |
| const res = await fetch('/api/growth'); |
| const data = await res.json(); |
| const creature = document.querySelector('#growthCreature'); |
| creature.className = 'creature ' + data.avatar; |
| creature.style.setProperty('--scale', data.size || 1); |
| document.querySelector('#growthStage').textContent = data.stage.toUpperCase() + ' [' + data.points + ' XP]'; |
| document.querySelector('#growthFill').style.width = Math.round(data.progress * 100) + '%'; |
| document.querySelector('#growthDesc').textContent = data.description; |
| document.querySelector('#growthStats').textContent = |
| 'MEMORIES: ' + data.stats.total_memories + |
| '\\nCHATS: ' + data.stats.interactions + |
| '\\nCONCEPTS: ' + data.stats.concepts + |
| '\\nREFLECTIONS: ' + data.stats.reflections; |
| } |
| document.querySelector('#form').onsubmit = async (e) => { |
| e.preventDefault(); |
| const input = document.querySelector('#input'); |
| const text = input.value.trim(); |
| if (!text) return; |
| input.value = ''; |
| add('user', text); |
| const data = await post('/api/chat', {message: text}); |
| add('bot', data.reply || data.error); |
| refreshGrowth(); |
| }; |
| document.querySelector('#mode').onchange = async (e) => { |
| const data = await post('/api/command', {command: 'mode', args: e.target.value}); |
| document.querySelector('#side').textContent = data.reply; |
| }; |
| document.querySelector('#status').onclick = async () => { |
| const data = await post('/api/command', {command: 'status', args: ''}); |
| document.querySelector('#side').textContent = data.reply; |
| }; |
| document.querySelector('#reflect').onclick = async () => { |
| const data = await post('/api/command', {command: 'reflect', args: ''}); |
| document.querySelector('#side').textContent = data.reply; |
| refreshGrowth(); |
| }; |
| document.querySelector('#search').onclick = async () => { |
| const data = await post('/api/command', {command: 'memory', args: document.querySelector('#query').value}); |
| document.querySelector('#side').textContent = data.reply; |
| }; |
| refreshGrowth(); |
| </script> |
| </body> |
| </html>""" |
|
|
|
|
| def chat_reply(message, session_res: SessionResources): |
| prompt, emotion, dissonance = build_chat_prompt( |
| message, |
| session_res.state, |
| session_res.memory, |
| goals_db=session_res.goals_db, |
| doc_store=session_res.doc_store, |
| prefs=session_res.state.prefs, |
| ) |
| output = session_res.brain.agent_turn(prompt, tools_enabled=True, raw_user_input=message) |
| try: |
| session_res.brain.evaluate_last(prompt, output) |
| except Exception: |
| pass |
| importance = min( |
| 0.95, 0.45 + emotion["intensity"] * 0.3 + dissonance["score"] * 0.15 |
| ) |
| session_res.memory.save_interaction( |
| message, |
| output, |
| mode=session_res.state.mode, |
| emotion=emotion, |
| importance=importance, |
| dissonance=dissonance, |
| ) |
| session_res.history.append(message, output, session_res.state.mode, emotion, dissonance) |
| session_res.state.turns += 1 |
| return output |
|
|
|
|
| app = Flask(__name__) |
| import secrets, os |
| app.config["SECRET_KEY"] = os.environ.get("DRIFT_SECRET_KEY", secrets.token_hex(32)) |
|
|
| socketio = SocketIO( |
| app, async_mode="gevent", cors_allowed_origins="*", websocket_compression=True |
| ) |
|
|
| broadcast_interval = 0.35 |
| total_bytes_raw = 0 |
| total_bytes_compressed = 0 |
| cognitive_orchestrator = CognitiveOrchestrator() |
|
|
| |
| trial_sessions = {} |
|
|
|
|
| def is_trial_active(session_id): |
| if not session_id or session_id not in trial_sessions: |
| return False |
| |
| if time.time() - trial_sessions[session_id] > 1800: |
| return False |
| return True |
|
|
|
|
| @app.route("/api/v1/system/governor-metrics", methods=["GET"]) |
| def governor_metrics(): |
| session_id = get_request_session_id() or "global" |
| session_res = get_session(session_id) |
| brain = session_res.brain |
| gov = getattr(brain, "_governor", None) |
| if gov is None: |
| return jsonify({ |
| "status": "error", |
| "detail": "RateGovernor not initialized. Call self.init_governor() in DriftBrain.__init__." |
| }), 503 |
| return jsonify({"status": "online", **gov.metrics_report()}) |
|
|
|
|
| @app.route("/expo") |
| def start_expo(): |
| """Alias for /trial — clean URL for sharing demo sessions.""" |
| return start_trial() |
|
|
|
|
| @app.route("/trial") |
| def start_trial(): |
| import uuid |
| from flask import make_response |
|
|
| session_id = str(uuid.uuid4()) |
| trial_sessions[session_id] = time.time() |
| |
| trial_html = INDEX_HTML.replace( |
| "document.querySelector('#form').onsubmit", |
| f"const DRIFT_SESSION_ID = '{session_id}';\n" |
| + "document.querySelector('#form').onsubmit", |
| ).replace( |
| "async function post(path, body={}) {", |
| "async function post(path, body={}) {\n if(typeof DRIFT_SESSION_ID !== 'undefined') body.session_id = DRIFT_SESSION_ID;", |
| ) |
| resp = make_response(trial_html) |
| resp.set_cookie("drift_session_id", session_id, max_age=1800, httponly=True, samesite="Lax") |
| return resp |
|
|
|
|
| def broadcast_observatory_state(): |
| global broadcast_interval, total_bytes_raw, total_bytes_compressed |
| while True: |
| try: |
| delta = cognitive_orchestrator.get_delta_state() |
| if len(delta) > 1: |
| raw_size = len(json.dumps(delta)) |
| total_bytes_raw += raw_size |
| total_bytes_compressed += int(raw_size * 0.3) |
| delta["network_stats"] = { |
| "raw_kb": round(total_bytes_raw / 1024, 2), |
| "comp_kb": round(total_bytes_compressed / 1024, 2), |
| "interval_ms": int(broadcast_interval * 1000), |
| } |
| socketio.emit("observatory_delta", delta) |
| except Exception: |
| pass |
| gevent.sleep(broadcast_interval) |
|
|
|
|
| threading.Thread(target=broadcast_observatory_state, daemon=True).start() |
|
|
|
|
| @socketio.on("latency_ping") |
| def handle_latency_ping(data): |
| emit( |
| "latency_pong", |
| {"server_time": time.time(), "client_timestamp": data.get("timestamp")}, |
| ) |
|
|
|
|
| @socketio.on("auto_adjust_rate") |
| def handle_adjust_rate(data): |
| global broadcast_interval |
| target_interval = data.get("interval", 0.35) |
| broadcast_interval = max(0.2, min(target_interval, 1.5)) |
|
|
|
|
| @app.route("/") |
| def index(): |
| from flask import make_response |
| session_id = request.cookies.get("drift_session_id") |
| had_cookie = True |
| if not session_id: |
| import uuid |
| session_id = str(uuid.uuid4()) |
| had_cookie = False |
| |
| |
| html = INDEX_HTML.replace( |
| "document.querySelector('#form').onsubmit", |
| f"const DRIFT_SESSION_ID = '{session_id}';\n" |
| + "document.querySelector('#form').onsubmit", |
| ).replace( |
| "async function post(path, body={}) {", |
| "async function post(path, body={}) {\n if(typeof DRIFT_SESSION_ID !== 'undefined') body.session_id = DRIFT_SESSION_ID;", |
| ) |
| |
| resp = make_response(html) |
| if not had_cookie: |
| resp.set_cookie("drift_session_id", session_id, max_age=30*86400, httponly=True, samesite="Lax") |
| return resp |
|
|
|
|
| @app.route("/api/growth", methods=["GET"]) |
| def get_growth(): |
| session_id = get_request_session_id() |
| session_res = get_session(session_id) |
| return jsonify(growth_profile(session_res.memory, session_res.state.turns)) |
|
|
|
|
| @app.route("/api/tags", methods=["GET"]) |
| def ollama_tags(): |
| return jsonify( |
| { |
| "models": [ |
| { |
| "name": "infj_bot:latest", |
| "model": "infj_bot:latest", |
| "modified_at": "2023-11-04T14:56:49.277302595-07:00", |
| "size": 7323310500, |
| "digest": "9f438cb9cd581fc025612d27f7c1a6669ff83a8bb0ed86c94fcf4c5440555697", |
| } |
| ] |
| } |
| ) |
|
|
|
|
| @app.route("/api/chat", methods=["POST"]) |
| def api_chat(): |
| payload = request.json or {} |
| session_id = get_request_session_id() |
|
|
| |
| if session_id and session_id in trial_sessions: |
| if not is_trial_active(session_id): |
| return jsonify( |
| {"error": "Trial session expired. Please start a new session at /trial"} |
| ), 403 |
|
|
| session_res = get_session(session_id) |
|
|
| |
| if "messages" in payload: |
| messages = payload.get("messages", []) |
| user_message = "" |
| for msg in reversed(messages): |
| if msg.get("role") == "user": |
| user_message = msg.get("content", "") |
| break |
| if not user_message: |
| return jsonify({"error": "No user message found"}), 400 |
|
|
| reply_text = chat_reply(user_message, session_res) |
|
|
| return jsonify( |
| { |
| "model": payload.get("model", "infj_bot:latest"), |
| "created_at": time.strftime("%Y-%m-%dT%H:%M:%S.000Z", time.gmtime()), |
| "message": {"role": "assistant", "content": reply_text}, |
| "done": True, |
| } |
| ) |
|
|
| |
| message = payload.get("message", "") |
| if isinstance(message, str): |
| message = message.strip() |
| if not message: |
| return jsonify({"error": "message is required"}), 400 |
| return jsonify({"reply": chat_reply(message, session_res)}) |
|
|
|
|
| @app.route("/v1/chat/completions", methods=["POST", "OPTIONS"]) |
| def openai_chat_completions(): |
| if request.method == "OPTIONS": |
| return "", 200 |
|
|
| payload = request.json or {} |
| session_id = get_request_session_id() |
|
|
| |
| if session_id and session_id in trial_sessions: |
| if not is_trial_active(session_id): |
| return jsonify( |
| {"error": "Trial session expired. Please start a new session at /trial"} |
| ), 403 |
|
|
| session_res = get_session(session_id) |
| |
| messages = payload.get("messages", []) |
|
|
| |
| user_message = "" |
| for msg in reversed(messages): |
| if msg.get("role") == "user": |
| user_message = msg.get("content", "") |
| break |
|
|
| if not user_message: |
| return jsonify({"error": "No user message found"}), 400 |
|
|
| |
| reply_text = chat_reply(user_message, session_res) |
|
|
| |
| import uuid |
|
|
| response = { |
| "id": f"chatcmpl-{uuid.uuid4().hex}", |
| "object": "chat.completion", |
| "created": int(time.time()), |
| "model": payload.get("model", "infj_bot"), |
| "choices": [ |
| { |
| "index": 0, |
| "message": {"role": "assistant", "content": reply_text}, |
| "finish_reason": "stop", |
| } |
| ], |
| "usage": {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0}, |
| } |
| return jsonify(response) |
|
|
|
|
| @app.route("/api/command", methods=["POST"]) |
| def api_command(): |
| payload = request.json or {} |
| session_id = get_request_session_id() |
| session_res = get_session(session_id) |
| |
| reply = handle_command( |
| payload.get("command", ""), |
| payload.get("args", ""), |
| session_res.state, |
| session_res.brain, |
| session_res.memory, |
| session_res.history, |
| session_res.goals_db, |
| session_res.doc_store, |
| ) |
| return jsonify({"reply": reply}) |
|
|
|
|
| @app.route("/api/email", methods=["POST"]) |
| def api_email(): |
| |
| return jsonify( |
| { |
| "sent": False, |
| "error": "Email sending not implemented (no backend configured).", |
| } |
| ), 501 |
|
|
|
|
| @app.route("/observatory") |
| def observatory(): |
| try: |
| path = Path(__file__).resolve().parent / "templates" / "observatory.html" |
| if not path.exists(): |
| path = Path("/home/crexs/templates/observatory.html") |
| with open(path, "r", encoding="utf-8") as f: |
| content = f.read() |
| from flask import make_response |
| resp = make_response(content) |
| resp.headers["Content-Type"] = "text/html" |
| return resp |
| except Exception: |
| app.logger.exception("observatory render failed") |
| return "Internal server error", 500 |
|
|
|
|
| @app.route("/glyph") |
| @app.route("/phi-glyph") |
| def glyph(): |
| try: |
| path = Path(__file__).resolve().parent / "templates" / "phi_glyph_system.html" |
| if not path.exists(): |
| path = Path("/home/crexs/Downloads/PHI Glyph System.html") |
| with open(path, "r", encoding="utf-8") as f: |
| content = f.read() |
| from flask import make_response |
| resp = make_response(content) |
| resp.headers["Content-Type"] = "text/html" |
| return resp |
| except Exception: |
| app.logger.exception("glyph render failed") |
| return "Internal server error", 500 |
|
|
|
|
| def main(): |
| import os |
|
|
| print("🚀 DRIFT Web App: Gevent + Compression + Delta Logic Active") |
| |
| |
| gevent.spawn(background_cognitive_loop) |
| gevent.spawn(prune_and_cleanup_sessions) |
| |
| port = int(os.getenv("PORT", 7860)) |
| socketio.run(app, host="0.0.0.0", port=port, debug=False, use_reloader=False) |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|