| import gradio as gr |
| import os |
| import json |
| import numpy as np |
| import soundfile as sf |
| from pathlib import Path |
| import threading |
| import time |
|
|
| |
| os.environ["HIVE_HOME"] = "/data/hive_data" |
| HIVE_HOME = Path(os.environ["HIVE_HOME"]) |
| HIVE_HOME.mkdir(parents=True, exist_ok=True) |
|
|
| |
| voice_authenticated = False |
| current_user = "guest" |
| hive_instance = None |
|
|
| |
| def get_hive(): |
| global hive_instance |
| if hive_instance is None: |
| from hive.core.bootstrap import Bootstrap |
| config_path = HIVE_HOME / "system" / "config.json" |
| bootstrap = Bootstrap(str(config_path)) |
| hive_instance = bootstrap.run() |
| return hive_instance |
|
|
| |
| def login(username, password, audio): |
| global voice_authenticated, current_user |
| try: |
| hive = get_hive() |
| |
| |
| if not hive.security.auth.check_password(username, password): |
| return "β Invalid credentials", False, False |
| |
| |
| if username == "owner": |
| if audio is None: |
| return "π€ Please speak to verify voice", False, False |
| audio_path = HIVE_HOME / "voice" / "verify.wav" |
| sf.write(str(audio_path), audio[1], audio[0]) |
| score = hive.voice.voiceprint.verify(audio_path, username) |
| if score < 0.7: |
| return f"β Voice match: {score:.2f} < 0.7", False, False |
| |
| voice_authenticated = True |
| current_user = username |
| return f"β
Logged in as {username}", True, False |
| except Exception as e: |
| return f"β Error: {str(e)}", False, False |
|
|
| def logout(): |
| global voice_authenticated, current_user |
| voice_authenticated = False |
| current_user = "guest" |
| return "π Logged out", False, True |
|
|
| |
| def process_voice(audio): |
| global current_user |
| if audio is None: |
| return None |
| |
| try: |
| hive = get_hive() |
| audio_path = HIVE_HOME / "voice" / "input.wav" |
| sf.write(str(audio_path), audio[1], audio[0]) |
| |
| |
| if not voice_authenticated: |
| user_id = hive.voice.voiceprint.identify(audio_path) |
| if user_id: |
| current_user = user_id |
| return f"ποΈ Welcome back, {user_id}!" |
| else: |
| return "ποΈ Unknown speaker. Say 'enroll' to register." |
| |
| |
| text = hive.voice.asr.transcribe(audio_path) |
| return text |
| except Exception as e: |
| return f"β Voice error: {str(e)}" |
|
|
| |
| def chat(message, history, audio_input): |
| global current_user |
| |
| |
| if audio_input is not None: |
| voice_result = process_voice(audio_input) |
| if voice_result and not voice_result.startswith("ποΈ"): |
| history = history + [[None, voice_result]] |
| yield history |
| return |
| |
| if voice_result and not voice_result.startswith("ποΈ Welcome"): |
| message = voice_result |
| elif voice_result and "enroll" in voice_result.lower(): |
| |
| hive = get_hive() |
| enroll_audio = HIVE_HOME / "voice" / "enroll.wav" |
| sf.write(str(enroll_audio), audio_input[1], audio_input[0]) |
| new_user_id = f"user_{int(time.time())}" |
| hive.voice.voiceprint.enroll(enroll_audio, new_user_id) |
| current_user = new_user_id |
| history = history + [[None, f"β
Enrolled as {new_user_id}"]] |
| yield history |
| return |
| |
| if not message or not message.strip(): |
| return history |
| |
| try: |
| hive = get_hive() |
| response = hive.convo.dialogue_manager.submit_turn( |
| session_id="default", |
| user_id=current_user, |
| raw_input={"text": message} |
| ) |
| |
| response_text = response.postprocessed_output.get("text", "Sorry, I couldn't process that.") |
| history = history + [[message, response_text]] |
| yield history |
| except Exception as e: |
| history = history + [[message, f"β Error: {str(e)}"]] |
| yield history |
|
|
| |
| def admin_rollback(): |
| try: |
| hive = get_hive() |
| result = hive.persistence.rollback() |
| return f"β
Rollback: {result}" |
| except Exception as e: |
| return f"β Rollback failed: {str(e)}" |
|
|
| def admin_snapshot(): |
| try: |
| hive = get_hive() |
| result = hive.persistence.save_snapshot() |
| return f"β
Snapshot: {result}" |
| except Exception as e: |
| return f"β Snapshot failed: {str(e)}" |
|
|
| def admin_logs(): |
| try: |
| logs_path = HIVE_HOME / "admin" / "audit.jsonl" |
| if logs_path.exists(): |
| return logs_path.read_text() |
| return "No logs found" |
| except Exception as e: |
| return f"β Error reading logs: {str(e)}" |
|
|
| |
| with gr.Blocks(title="π Hive β Local AI Tutor", css=""" |
| .login-box { background: #f0f0f0; padding: 20px; border-radius: 10px; margin: 10px; } |
| .admin-tab { background: #fff3cd; } |
| """) as demo: |
| |
| |
| logged_in = gr.State(False) |
| |
| |
| with gr.Row(): |
| with gr.Column(scale=3): |
| gr.Markdown("# π Hive β Your Private AI Tutor") |
| gr.Markdown("*Voice-first, offline-capable AI assistant with self-optimizing memory*") |
| with gr.Column(scale=1): |
| with gr.Group(elem_classes=["login-box"]): |
| username = gr.Textbox(label="Username", value="owner") |
| password = gr.Textbox(label="Password", type="password", value="Fehr2008") |
| login_audio = gr.Audio(source="microphone", type="numpy", label="Verify voice (owner)") |
| login_btn = gr.Button("Login", variant="primary") |
| logout_btn = gr.Button("Logout", visible=False) |
| status = gr.Text(label="Status", interactive=False) |
| |
| |
| with gr.Row(): |
| with gr.Column(scale=4): |
| chatbot = gr.Chatbot(height=500, label="Conversation") |
| with gr.Row(): |
| msg = gr.Textbox(label="Message", placeholder="Type or speak...") |
| audio = gr.Audio(source="microphone", type="numpy", label="π€") |
| send = gr.Button("Send") |
| with gr.Column(scale=1, visible=False) as admin_col: |
| with gr.Tab("Admin", elem_classes=["admin-tab"]): |
| gr.Markdown("### π Admin Controls") |
| with gr.Row(): |
| rollback_btn = gr.Button("βͺ Force Rollback", variant="secondary") |
| snapshot_btn = gr.Button("πΎ Save Snapshot", variant="secondary") |
| admin_status = gr.Text(label="Result") |
| gr.Markdown("### π Audit Logs") |
| logs = gr.TextArea(label="Logs", lines=20, max_lines=30) |
| refresh_logs = gr.Button("π Refresh") |
| |
| |
| login_btn.click( |
| login, |
| inputs=[username, password, login_audio], |
| outputs=[status, logged_in, admin_col] |
| ).then( |
| lambda: (gr.update(visible=False), gr.update(visible=True)), |
| outputs=[login_btn, logout_btn] |
| ) |
| |
| logout_btn.click( |
| logout, |
| outputs=[status, logged_in, admin_col] |
| ).then( |
| lambda: (gr.update(visible=True), gr.update(visible=False)), |
| outputs=[login_btn, logout_btn] |
| ) |
| |
| |
| msg.submit(chat, [msg, chatbot, audio], chatbot).then(lambda: "", outputs=msg) |
| send.click(chat, [msg, chatbot, audio], chatbot).then(lambda: "", outputs=msg) |
| audio.stop_recording( |
| chat, [msg, chatbot, audio], chatbot |
| ) |
| |
| |
| rollback_btn.click(admin_rollback, outputs=admin_status) |
| snapshot_btn.click(admin_snapshot, outputs=admin_status) |
| refresh_logs.click(admin_logs, outputs=logs) |
|
|
| |
| if __name__ == "__main__": |
| demo.launch(server_name="0.0.0.0", server_port=7860, share=False) |