Paulhayes commited on
Commit
705d649
Β·
verified Β·
1 Parent(s): 6cae13c

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +232 -0
app.py ADDED
@@ -0,0 +1,232 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ import json
4
+ import numpy as np
5
+ import soundfile as sf
6
+ from pathlib import Path
7
+ import threading
8
+ import time
9
+
10
+ # Set up Hive home
11
+ os.environ["HIVE_HOME"] = "/data/hive_data"
12
+ HIVE_HOME = Path(os.environ["HIVE_HOME"])
13
+ HIVE_HOME.mkdir(parents=True, exist_ok=True)
14
+
15
+ # Global state
16
+ voice_authenticated = False
17
+ current_user = "guest"
18
+ hive_instance = None
19
+
20
+ # Lazy initialization
21
+ def get_hive():
22
+ global hive_instance
23
+ if hive_instance is None:
24
+ from hive.core.bootstrap import Bootstrap
25
+ config_path = HIVE_HOME / "system" / "config.json"
26
+ bootstrap = Bootstrap(str(config_path))
27
+ hive_instance = bootstrap.run()
28
+ return hive_instance
29
+
30
+ # Authentication functions
31
+ def login(username, password, audio):
32
+ global voice_authenticated, current_user
33
+ try:
34
+ hive = get_hive()
35
+
36
+ # Check password
37
+ if not hive.security.auth.check_password(username, password):
38
+ return "❌ Invalid credentials", False, False
39
+
40
+ # Check voice for owner
41
+ if username == "owner":
42
+ if audio is None:
43
+ return "🎀 Please speak to verify voice", False, False
44
+ audio_path = HIVE_HOME / "voice" / "verify.wav"
45
+ sf.write(str(audio_path), audio[1], audio[0])
46
+ score = hive.voice.voiceprint.verify(audio_path, username)
47
+ if score < 0.7:
48
+ return f"❌ Voice match: {score:.2f} < 0.7", False, False
49
+
50
+ voice_authenticated = True
51
+ current_user = username
52
+ return f"βœ… Logged in as {username}", True, False
53
+ except Exception as e:
54
+ return f"❌ Error: {str(e)}", False, False
55
+
56
+ def logout():
57
+ global voice_authenticated, current_user
58
+ voice_authenticated = False
59
+ current_user = "guest"
60
+ return "πŸ‘‹ Logged out", False, True
61
+
62
+ # Voice processing
63
+ def process_voice(audio):
64
+ global current_user
65
+ if audio is None:
66
+ return None
67
+
68
+ try:
69
+ hive = get_hive()
70
+ audio_path = HIVE_HOME / "voice" / "input.wav"
71
+ sf.write(str(audio_path), audio[1], audio[0])
72
+
73
+ # Authenticate or identify
74
+ if not voice_authenticated:
75
+ user_id = hive.voice.voiceprint.identify(audio_path)
76
+ if user_id:
77
+ current_user = user_id
78
+ return f"πŸŽ™οΈ Welcome back, {user_id}!"
79
+ else:
80
+ return "πŸŽ™οΈ Unknown speaker. Say 'enroll' to register."
81
+
82
+ # Transcribe
83
+ text = hive.voice.asr.transcribe(audio_path)
84
+ return text
85
+ except Exception as e:
86
+ return f"❌ Voice error: {str(e)}"
87
+
88
+ # Chat function
89
+ def chat(message, history, audio_input):
90
+ global current_user
91
+
92
+ # Process voice if provided
93
+ if audio_input is not None:
94
+ voice_result = process_voice(audio_input)
95
+ if voice_result and not voice_result.startswith("πŸŽ™οΈ"):
96
+ history = history + [[None, voice_result]]
97
+ yield history
98
+ return
99
+
100
+ if voice_result and not voice_result.startswith("πŸŽ™οΈ Welcome"):
101
+ message = voice_result
102
+ elif voice_result and "enroll" in voice_result.lower():
103
+ # Start enrollment
104
+ hive = get_hive()
105
+ enroll_audio = HIVE_HOME / "voice" / "enroll.wav"
106
+ sf.write(str(enroll_audio), audio_input[1], audio_input[0])
107
+ new_user_id = f"user_{int(time.time())}"
108
+ hive.voice.voiceprint.enroll(enroll_audio, new_user_id)
109
+ current_user = new_user_id
110
+ history = history + [[None, f"βœ… Enrolled as {new_user_id}"]]
111
+ yield history
112
+ return
113
+
114
+ if not message or not message.strip():
115
+ return history
116
+
117
+ try:
118
+ hive = get_hive()
119
+ response = hive.convo.dialogue_manager.submit_turn(
120
+ session_id="default",
121
+ user_id=current_user,
122
+ raw_input={"text": message}
123
+ )
124
+
125
+ response_text = response.postprocessed_output.get("text", "Sorry, I couldn't process that.")
126
+ history = history + [[message, response_text]]
127
+ yield history
128
+ except Exception as e:
129
+ history = history + [[message, f"❌ Error: {str(e)}"]]
130
+ yield history
131
+
132
+ # Admin functions
133
+ def admin_rollback():
134
+ try:
135
+ hive = get_hive()
136
+ result = hive.persistence.rollback()
137
+ return f"βœ… Rollback: {result}"
138
+ except Exception as e:
139
+ return f"❌ Rollback failed: {str(e)}"
140
+
141
+ def admin_snapshot():
142
+ try:
143
+ hive = get_hive()
144
+ result = hive.persistence.save_snapshot()
145
+ return f"βœ… Snapshot: {result}"
146
+ except Exception as e:
147
+ return f"❌ Snapshot failed: {str(e)}"
148
+
149
+ def admin_logs():
150
+ try:
151
+ logs_path = HIVE_HOME / "admin" / "audit.jsonl"
152
+ if logs_path.exists():
153
+ return logs_path.read_text()
154
+ return "No logs found"
155
+ except Exception as e:
156
+ return f"❌ Error reading logs: {str(e)}"
157
+
158
+ # Build UI
159
+ with gr.Blocks(title="🐝 Hive β€” Local AI Tutor", css="""
160
+ .login-box { background: #f0f0f0; padding: 20px; border-radius: 10px; margin: 10px; }
161
+ .admin-tab { background: #fff3cd; }
162
+ """) as demo:
163
+
164
+ # State
165
+ logged_in = gr.State(False)
166
+
167
+ # Login section
168
+ with gr.Row():
169
+ with gr.Column(scale=3):
170
+ gr.Markdown("# 🐝 Hive β€” Your Private AI Tutor")
171
+ gr.Markdown("*Voice-first, offline-capable AI assistant with self-optimizing memory*")
172
+ with gr.Column(scale=1):
173
+ with gr.Group(elem_classes=["login-box"]):
174
+ username = gr.Textbox(label="Username", value="owner")
175
+ password = gr.Textbox(label="Password", type="password", value="Fehr2008")
176
+ login_audio = gr.Audio(source="microphone", type="numpy", label="Verify voice (owner)")
177
+ login_btn = gr.Button("Login", variant="primary")
178
+ logout_btn = gr.Button("Logout", visible=False)
179
+ status = gr.Text(label="Status", interactive=False)
180
+
181
+ # Main chat interface
182
+ with gr.Row():
183
+ with gr.Column(scale=4):
184
+ chatbot = gr.Chatbot(height=500, label="Conversation")
185
+ with gr.Row():
186
+ msg = gr.Textbox(label="Message", placeholder="Type or speak...")
187
+ audio = gr.Audio(source="microphone", type="numpy", label="🎀")
188
+ send = gr.Button("Send")
189
+ with gr.Column(scale=1, visible=False) as admin_col:
190
+ with gr.Tab("Admin", elem_classes=["admin-tab"]):
191
+ gr.Markdown("### πŸ” Admin Controls")
192
+ with gr.Row():
193
+ rollback_btn = gr.Button("βͺ Force Rollback", variant="secondary")
194
+ snapshot_btn = gr.Button("πŸ’Ύ Save Snapshot", variant="secondary")
195
+ admin_status = gr.Text(label="Result")
196
+ gr.Markdown("### πŸ“‹ Audit Logs")
197
+ logs = gr.TextArea(label="Logs", lines=20, max_lines=30)
198
+ refresh_logs = gr.Button("πŸ”„ Refresh")
199
+
200
+ # Event handlers
201
+ login_btn.click(
202
+ login,
203
+ inputs=[username, password, login_audio],
204
+ outputs=[status, logged_in, admin_col]
205
+ ).then(
206
+ lambda: (gr.update(visible=False), gr.update(visible=True)),
207
+ outputs=[login_btn, logout_btn]
208
+ )
209
+
210
+ logout_btn.click(
211
+ logout,
212
+ outputs=[status, logged_in, admin_col]
213
+ ).then(
214
+ lambda: (gr.update(visible=True), gr.update(visible=False)),
215
+ outputs=[login_btn, logout_btn]
216
+ )
217
+
218
+ # Chat events
219
+ msg.submit(chat, [msg, chatbot, audio], chatbot).then(lambda: "", outputs=msg)
220
+ send.click(chat, [msg, chatbot, audio], chatbot).then(lambda: "", outputs=msg)
221
+ audio.stop_recording(
222
+ chat, [msg, chatbot, audio], chatbot
223
+ )
224
+
225
+ # Admin events
226
+ rollback_btn.click(admin_rollback, outputs=admin_status)
227
+ snapshot_btn.click(admin_snapshot, outputs=admin_status)
228
+ refresh_logs.click(admin_logs, outputs=logs)
229
+
230
+ # Launch
231
+ if __name__ == "__main__":
232
+ demo.launch(server_name="0.0.0.0", server_port=7860, share=False)