abstractassistant 0.2.7__py3-none-any.whl → 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- abstractassistant/app.py +290 -179
- abstractassistant/core/llm_manager.py +189 -19
- abstractassistant/core/tts_manager.py +75 -25
- abstractassistant/ui/history_dialog.py +5 -5
- abstractassistant/ui/qt_bubble.py +291 -121
- abstractassistant/ui/toast_window.py +14 -15
- abstractassistant/ui/ui_styles.py +2 -2
- abstractassistant/utils/icon_generator.py +269 -139
- abstractassistant/utils/markdown_renderer.py +1 -1
- {abstractassistant-0.2.7.dist-info → abstractassistant-0.3.0.dist-info}/METADATA +12 -14
- abstractassistant-0.3.0.dist-info/RECORD +28 -0
- setup_macos_app.py +64 -10
- abstractassistant-0.2.7.dist-info/RECORD +0 -28
- {abstractassistant-0.2.7.dist-info → abstractassistant-0.3.0.dist-info}/WHEEL +0 -0
- {abstractassistant-0.2.7.dist-info → abstractassistant-0.3.0.dist-info}/entry_points.txt +0 -0
- {abstractassistant-0.2.7.dist-info → abstractassistant-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {abstractassistant-0.2.7.dist-info → abstractassistant-0.3.0.dist-info}/top_level.txt +0 -0
|
@@ -70,30 +70,109 @@ class LLMManager:
|
|
|
70
70
|
self._initialize_llm()
|
|
71
71
|
|
|
72
72
|
def _initialize_llm(self):
|
|
73
|
-
"""Initialize the LLM with current provider and model.
|
|
73
|
+
"""Initialize the LLM with current provider and model.
|
|
74
|
+
|
|
75
|
+
CRITICAL: This method ONLY initializes the LLM provider connection.
|
|
76
|
+
It does NOT create a new session to preserve chat history.
|
|
77
|
+
Sessions are only created when explicitly requested.
|
|
78
|
+
"""
|
|
74
79
|
try:
|
|
75
80
|
if self.debug:
|
|
76
|
-
|
|
81
|
+
if self.debug:
|
|
82
|
+
print(f"🔄 Creating LLM with provider={self.current_provider}, model={self.current_model}")
|
|
83
|
+
|
|
84
|
+
old_llm = self.llm # Keep reference to old LLM
|
|
77
85
|
self.llm = create_llm(
|
|
78
86
|
self.current_provider,
|
|
79
87
|
model=self.current_model,
|
|
80
88
|
execute_tools=True # Enable automatic tool execution
|
|
81
89
|
)
|
|
82
90
|
if self.debug:
|
|
83
|
-
|
|
91
|
+
if self.debug:
|
|
92
|
+
print(f"✅ LLM created successfully")
|
|
84
93
|
|
|
85
|
-
#
|
|
86
|
-
|
|
94
|
+
# CRITICAL FIX: Only create a session if we don't have one yet
|
|
95
|
+
# This preserves existing sessions when switching providers/models
|
|
96
|
+
if self.current_session is None:
|
|
97
|
+
if self.debug:
|
|
98
|
+
if self.debug:
|
|
99
|
+
print("🆕 No existing session - creating initial session")
|
|
100
|
+
self.create_new_session()
|
|
101
|
+
else:
|
|
102
|
+
# Update existing session with new LLM while preserving history
|
|
103
|
+
if self.debug:
|
|
104
|
+
if self.debug:
|
|
105
|
+
print("🔄 Updating existing session with new LLM (preserving history)")
|
|
106
|
+
self._update_session_llm()
|
|
87
107
|
|
|
88
108
|
# Use AbstractCore's built-in token detection
|
|
89
109
|
self._update_token_limits_from_abstractcore()
|
|
90
110
|
|
|
91
111
|
except Exception as e:
|
|
92
|
-
|
|
112
|
+
if self.debug:
|
|
113
|
+
print(f"❌ Error initializing LLM: {e}")
|
|
93
114
|
import traceback
|
|
94
115
|
traceback.print_exc()
|
|
95
116
|
# Keep previous LLM if initialization fails
|
|
96
117
|
|
|
118
|
+
def _update_session_llm(self):
|
|
119
|
+
"""Update existing session with new LLM while preserving message history.
|
|
120
|
+
|
|
121
|
+
This method allows switching providers/models without losing chat history.
|
|
122
|
+
"""
|
|
123
|
+
if not self.current_session or not self.llm:
|
|
124
|
+
return
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
# Get current session messages to preserve history
|
|
128
|
+
existing_messages = getattr(self.current_session, 'messages', [])
|
|
129
|
+
existing_system_prompt = getattr(self.current_session, 'system_prompt', None)
|
|
130
|
+
|
|
131
|
+
# Prepare tools list (same as in create_new_session)
|
|
132
|
+
tools = []
|
|
133
|
+
if TOOLS_AVAILABLE:
|
|
134
|
+
tools = [
|
|
135
|
+
list_files, search_files, read_file, edit_file,
|
|
136
|
+
write_file, execute_command, web_search
|
|
137
|
+
]
|
|
138
|
+
|
|
139
|
+
# Create new session with new LLM but preserve system prompt
|
|
140
|
+
system_prompt = existing_system_prompt or (
|
|
141
|
+
"""
|
|
142
|
+
You are a helpful AI assistant who has access to tools to help the user.
|
|
143
|
+
Always be a critical and creative thinker who leverage constructive skepticism to progress and evolve its reasoning and answers.
|
|
144
|
+
Always answer in nicely formatted markdown.
|
|
145
|
+
"""
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
# Create new session with preserved system prompt
|
|
149
|
+
new_session = BasicSession(
|
|
150
|
+
self.llm,
|
|
151
|
+
system_prompt=system_prompt,
|
|
152
|
+
tools=tools
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
# Restore message history by replaying messages
|
|
156
|
+
# Skip system message (first message) as it's already set
|
|
157
|
+
for msg in existing_messages[1:] if len(existing_messages) > 1 else []:
|
|
158
|
+
if hasattr(msg, 'role') and hasattr(msg, 'content'):
|
|
159
|
+
# Add message to new session's history without generating response
|
|
160
|
+
new_session.messages.append(msg)
|
|
161
|
+
|
|
162
|
+
# Replace current session
|
|
163
|
+
self.current_session = new_session
|
|
164
|
+
|
|
165
|
+
if self.debug:
|
|
166
|
+
if self.debug:
|
|
167
|
+
print(f"🔄 Session updated with new LLM - preserved {len(existing_messages)} messages")
|
|
168
|
+
|
|
169
|
+
except Exception as e:
|
|
170
|
+
if self.debug:
|
|
171
|
+
if self.debug:
|
|
172
|
+
print(f"❌ Error updating session LLM (preserving existing session): {e}")
|
|
173
|
+
# CRITICAL: Do NOT create new session on error - preserve existing session
|
|
174
|
+
# The user's chat history is more important than a perfect LLM update
|
|
175
|
+
|
|
97
176
|
def _update_token_limits_from_abstractcore(self):
|
|
98
177
|
"""Update token limits using AbstractCore's built-in detection."""
|
|
99
178
|
if self.llm:
|
|
@@ -104,16 +183,21 @@ class LLMManager:
|
|
|
104
183
|
|
|
105
184
|
if self.debug:
|
|
106
185
|
# Show AbstractCore's token configuration
|
|
107
|
-
|
|
186
|
+
if self.debug:
|
|
187
|
+
print(f"📊 {self.llm.get_token_configuration_summary()}")
|
|
108
188
|
|
|
109
189
|
def create_new_session(self, tts_mode: bool = False):
|
|
110
190
|
"""Create a new session with tools - CLEAN AND SIMPLE as per AbstractCore docs.
|
|
111
191
|
|
|
192
|
+
WARNING: This method creates a completely new session, destroying existing chat history.
|
|
193
|
+
Use update_session_mode() to switch TTS mode while preserving history.
|
|
194
|
+
|
|
112
195
|
Args:
|
|
113
196
|
tts_mode: If True, use concise prompts optimized for text-to-speech
|
|
114
197
|
"""
|
|
115
198
|
if not self.llm:
|
|
116
|
-
|
|
199
|
+
if self.debug:
|
|
200
|
+
print("❌ No LLM available - cannot create session")
|
|
117
201
|
return
|
|
118
202
|
|
|
119
203
|
# Prepare tools list
|
|
@@ -124,7 +208,8 @@ class LLMManager:
|
|
|
124
208
|
write_file, execute_command, web_search
|
|
125
209
|
]
|
|
126
210
|
if self.debug:
|
|
127
|
-
|
|
211
|
+
if self.debug:
|
|
212
|
+
print(f"🔧 Registering {len(tools)} tools with session")
|
|
128
213
|
|
|
129
214
|
# Choose system prompt based on TTS mode
|
|
130
215
|
if tts_mode:
|
|
@@ -155,9 +240,84 @@ class LLMManager:
|
|
|
155
240
|
|
|
156
241
|
if self.debug:
|
|
157
242
|
if TOOLS_AVAILABLE:
|
|
158
|
-
|
|
243
|
+
if self.debug:
|
|
244
|
+
print(f"✅ Created new AbstractCore session with tools ({'TTS mode' if tts_mode else 'normal mode'})")
|
|
245
|
+
else:
|
|
246
|
+
if self.debug:
|
|
247
|
+
print(f"✅ Created new AbstractCore session (no tools available, {'TTS mode' if tts_mode else 'normal mode'})")
|
|
248
|
+
|
|
249
|
+
def update_session_mode(self, tts_mode: bool = False):
|
|
250
|
+
"""Update session mode (TTS vs normal) while preserving chat history.
|
|
251
|
+
|
|
252
|
+
This method changes the system prompt behavior without destroying the session.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
tts_mode: If True, switch to TTS-optimized mode; if False, switch to normal mode
|
|
256
|
+
"""
|
|
257
|
+
if not self.current_session:
|
|
258
|
+
# No existing session - only create if this is initial startup
|
|
259
|
+
if self.debug:
|
|
260
|
+
if self.debug:
|
|
261
|
+
print("⚠️ No session exists - creating initial session for mode update")
|
|
262
|
+
self.create_new_session(tts_mode=tts_mode)
|
|
263
|
+
return
|
|
264
|
+
|
|
265
|
+
try:
|
|
266
|
+
# Get current session messages to preserve history
|
|
267
|
+
existing_messages = getattr(self.current_session, 'messages', [])
|
|
268
|
+
|
|
269
|
+
# Prepare tools list
|
|
270
|
+
tools = []
|
|
271
|
+
if TOOLS_AVAILABLE:
|
|
272
|
+
tools = [
|
|
273
|
+
list_files, search_files, read_file, edit_file,
|
|
274
|
+
write_file, execute_command, web_search
|
|
275
|
+
]
|
|
276
|
+
|
|
277
|
+
# Choose system prompt based on TTS mode
|
|
278
|
+
if tts_mode:
|
|
279
|
+
system_prompt = (
|
|
280
|
+
"""
|
|
281
|
+
You are a Helpful Voice Assistant. By design, your answers are short and more conversational, unless specifically asked to detail something.
|
|
282
|
+
You only speak, so never use any text formatting or markdown. Write for a speaker.
|
|
283
|
+
"""
|
|
284
|
+
)
|
|
159
285
|
else:
|
|
160
|
-
|
|
286
|
+
system_prompt = (
|
|
287
|
+
"""
|
|
288
|
+
You are a helpful AI assistant who has access to tools to help the user.
|
|
289
|
+
Always be a critical and creative thinker who leverage constructive skepticism to progress and evolve its reasoning and answers.
|
|
290
|
+
Always answer in nicely formatted markdown.
|
|
291
|
+
"""
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
# Create new session with updated system prompt
|
|
295
|
+
new_session = BasicSession(
|
|
296
|
+
self.llm,
|
|
297
|
+
system_prompt=system_prompt,
|
|
298
|
+
tools=tools
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
# Restore message history by replaying messages
|
|
302
|
+
# Skip system message (first message) as it's already set with new prompt
|
|
303
|
+
for msg in existing_messages[1:] if len(existing_messages) > 1 else []:
|
|
304
|
+
if hasattr(msg, 'role') and hasattr(msg, 'content'):
|
|
305
|
+
# Add message to new session's history without generating response
|
|
306
|
+
new_session.messages.append(msg)
|
|
307
|
+
|
|
308
|
+
# Replace current session
|
|
309
|
+
self.current_session = new_session
|
|
310
|
+
|
|
311
|
+
if self.debug:
|
|
312
|
+
if self.debug:
|
|
313
|
+
print(f"🔄 Session mode updated to {'TTS' if tts_mode else 'normal'} - preserved {len(existing_messages)} messages")
|
|
314
|
+
|
|
315
|
+
except Exception as e:
|
|
316
|
+
if self.debug:
|
|
317
|
+
if self.debug:
|
|
318
|
+
print(f"❌ Error updating session mode (preserving existing session): {e}")
|
|
319
|
+
# CRITICAL: Do NOT create new session on error - preserve existing session
|
|
320
|
+
# The user's chat history is more important than a perfect mode switch
|
|
161
321
|
|
|
162
322
|
def clear_session(self):
|
|
163
323
|
"""Clear current session and create a new one."""
|
|
@@ -168,19 +328,22 @@ class LLMManager:
|
|
|
168
328
|
try:
|
|
169
329
|
if not self.current_session:
|
|
170
330
|
if self.debug:
|
|
171
|
-
|
|
331
|
+
if self.debug:
|
|
332
|
+
print("⚠️ No session to save")
|
|
172
333
|
return False
|
|
173
334
|
|
|
174
335
|
# Use AbstractCore's built-in save method
|
|
175
336
|
self.current_session.save(filepath)
|
|
176
337
|
|
|
177
338
|
if self.debug:
|
|
178
|
-
|
|
339
|
+
if self.debug:
|
|
340
|
+
print(f"✅ Session saved to {filepath}")
|
|
179
341
|
return True
|
|
180
342
|
|
|
181
343
|
except Exception as e:
|
|
182
344
|
if self.debug:
|
|
183
|
-
|
|
345
|
+
if self.debug:
|
|
346
|
+
print(f"❌ Error saving session: {e}")
|
|
184
347
|
return False
|
|
185
348
|
|
|
186
349
|
def load_session(self, filepath: str):
|
|
@@ -201,12 +364,14 @@ class LLMManager:
|
|
|
201
364
|
self._update_token_limits_from_abstractcore()
|
|
202
365
|
|
|
203
366
|
if self.debug:
|
|
204
|
-
|
|
367
|
+
if self.debug:
|
|
368
|
+
print(f"✅ Session loaded from {filepath}")
|
|
205
369
|
return True
|
|
206
370
|
|
|
207
371
|
except Exception as e:
|
|
208
372
|
if self.debug:
|
|
209
|
-
|
|
373
|
+
if self.debug:
|
|
374
|
+
print(f"❌ Error loading session: {e}")
|
|
210
375
|
return False
|
|
211
376
|
|
|
212
377
|
def get_providers(self) -> List[Dict[str, Any]]:
|
|
@@ -219,7 +384,8 @@ class LLMManager:
|
|
|
219
384
|
return get_available_models_for_provider(provider)
|
|
220
385
|
except Exception as e:
|
|
221
386
|
if self.debug:
|
|
222
|
-
|
|
387
|
+
if self.debug:
|
|
388
|
+
print(f"⚠️ Could not get models for {provider}: {e}")
|
|
223
389
|
return []
|
|
224
390
|
|
|
225
391
|
def set_provider(self, provider: str, model: Optional[str] = None):
|
|
@@ -235,7 +401,8 @@ class LLMManager:
|
|
|
235
401
|
# Reinitialize LLM
|
|
236
402
|
self._initialize_llm()
|
|
237
403
|
elif self.debug:
|
|
238
|
-
|
|
404
|
+
if self.debug:
|
|
405
|
+
print(f"⚠️ Provider {provider} not available")
|
|
239
406
|
|
|
240
407
|
def set_model(self, model: str):
|
|
241
408
|
"""Set the active model for current provider."""
|
|
@@ -261,8 +428,11 @@ class LLMManager:
|
|
|
261
428
|
self.set_model(model)
|
|
262
429
|
|
|
263
430
|
try:
|
|
264
|
-
# Ensure we have a session
|
|
431
|
+
# Ensure we have a session - but only create if absolutely necessary
|
|
265
432
|
if self.current_session is None:
|
|
433
|
+
if self.debug:
|
|
434
|
+
if self.debug:
|
|
435
|
+
print("⚠️ No session exists - creating initial session for first use")
|
|
266
436
|
self.create_new_session()
|
|
267
437
|
|
|
268
438
|
# Generate response using session with optional media files
|
|
@@ -23,16 +23,43 @@ class VoiceManager:
|
|
|
23
23
|
"""
|
|
24
24
|
self.debug_mode = debug_mode
|
|
25
25
|
self._abstractvoice_manager = None
|
|
26
|
+
|
|
27
|
+
# Callbacks for speech start/end events
|
|
28
|
+
self.on_speech_start = None
|
|
29
|
+
self.on_speech_end = None
|
|
26
30
|
|
|
27
31
|
try:
|
|
28
32
|
self._abstractvoice_manager = AbstractVoiceManager(debug_mode=debug_mode)
|
|
33
|
+
|
|
34
|
+
# Set up NEW v0.5.1 precise audio callbacks (not synthesis callbacks)
|
|
35
|
+
self._abstractvoice_manager.on_audio_start = self._on_audio_start
|
|
36
|
+
self._abstractvoice_manager.on_audio_end = self._on_audio_end
|
|
37
|
+
|
|
29
38
|
if self.debug_mode:
|
|
30
|
-
|
|
39
|
+
if self.debug_mode:
|
|
40
|
+
print("🔊 AbstractVoice v0.5.1 initialized with precise audio callbacks")
|
|
31
41
|
except Exception as e:
|
|
32
42
|
if self.debug_mode:
|
|
33
|
-
|
|
43
|
+
if self.debug_mode:
|
|
44
|
+
print(f"❌ AbstractVoice initialization failed: {e}")
|
|
34
45
|
raise RuntimeError(f"Failed to initialize AbstractVoice: {e}")
|
|
35
46
|
|
|
47
|
+
def _on_audio_start(self):
|
|
48
|
+
"""Called when audio stream actually starts playing (v0.5.1 precise timing)."""
|
|
49
|
+
if self.debug_mode:
|
|
50
|
+
if self.debug_mode:
|
|
51
|
+
print("🔊 Audio stream started - user can hear speech")
|
|
52
|
+
if self.on_speech_start:
|
|
53
|
+
self.on_speech_start()
|
|
54
|
+
|
|
55
|
+
def _on_audio_end(self):
|
|
56
|
+
"""Called when audio stream actually ends (v0.5.1 precise timing)."""
|
|
57
|
+
if self.debug_mode:
|
|
58
|
+
if self.debug_mode:
|
|
59
|
+
print("🔊 Audio stream ended - ready for next action")
|
|
60
|
+
if self.on_speech_end:
|
|
61
|
+
self.on_speech_end()
|
|
62
|
+
|
|
36
63
|
def is_available(self) -> bool:
|
|
37
64
|
"""Check if TTS is available."""
|
|
38
65
|
return True # AbstractVoice is a required dependency, always available after construction
|
|
@@ -54,7 +81,8 @@ class VoiceManager:
|
|
|
54
81
|
"""
|
|
55
82
|
if not text.strip():
|
|
56
83
|
if self.debug_mode:
|
|
57
|
-
|
|
84
|
+
if self.debug_mode:
|
|
85
|
+
print("❌ Empty text provided to TTS")
|
|
58
86
|
return False
|
|
59
87
|
|
|
60
88
|
try:
|
|
@@ -62,7 +90,8 @@ class VoiceManager:
|
|
|
62
90
|
return True
|
|
63
91
|
except Exception as e:
|
|
64
92
|
if self.debug_mode:
|
|
65
|
-
|
|
93
|
+
if self.debug_mode:
|
|
94
|
+
print(f"❌ AbstractVoice speak error: {e}")
|
|
66
95
|
return False
|
|
67
96
|
|
|
68
97
|
def pause(self) -> bool:
|
|
@@ -74,11 +103,13 @@ class VoiceManager:
|
|
|
74
103
|
try:
|
|
75
104
|
success = self._abstractvoice_manager.pause_speaking()
|
|
76
105
|
if self.debug_mode:
|
|
77
|
-
|
|
106
|
+
if self.debug_mode:
|
|
107
|
+
print(f"🔊 AbstractVoice speech {'paused' if success else 'pause failed'}")
|
|
78
108
|
return success
|
|
79
109
|
except Exception as e:
|
|
80
110
|
if self.debug_mode:
|
|
81
|
-
|
|
111
|
+
if self.debug_mode:
|
|
112
|
+
print(f"❌ Error pausing AbstractVoice: {e}")
|
|
82
113
|
return False
|
|
83
114
|
|
|
84
115
|
def resume(self) -> bool:
|
|
@@ -90,11 +121,13 @@ class VoiceManager:
|
|
|
90
121
|
try:
|
|
91
122
|
success = self._abstractvoice_manager.resume_speaking()
|
|
92
123
|
if self.debug_mode:
|
|
93
|
-
|
|
124
|
+
if self.debug_mode:
|
|
125
|
+
print(f"🔊 AbstractVoice speech {'resumed' if success else 'resume failed'}")
|
|
94
126
|
return success
|
|
95
127
|
except Exception as e:
|
|
96
128
|
if self.debug_mode:
|
|
97
|
-
|
|
129
|
+
if self.debug_mode:
|
|
130
|
+
print(f"❌ Error resuming AbstractVoice: {e}")
|
|
98
131
|
return False
|
|
99
132
|
|
|
100
133
|
def is_paused(self) -> bool:
|
|
@@ -103,7 +136,8 @@ class VoiceManager:
|
|
|
103
136
|
return self._abstractvoice_manager.is_paused()
|
|
104
137
|
except Exception as e:
|
|
105
138
|
if self.debug_mode:
|
|
106
|
-
|
|
139
|
+
if self.debug_mode:
|
|
140
|
+
print(f"❌ Error checking pause state: {e}")
|
|
107
141
|
return False
|
|
108
142
|
|
|
109
143
|
def get_state(self) -> str:
|
|
@@ -121,7 +155,8 @@ class VoiceManager:
|
|
|
121
155
|
return 'idle'
|
|
122
156
|
except Exception as e:
|
|
123
157
|
if self.debug_mode:
|
|
124
|
-
|
|
158
|
+
if self.debug_mode:
|
|
159
|
+
print(f"❌ Error getting TTS state: {e}")
|
|
125
160
|
return 'idle'
|
|
126
161
|
|
|
127
162
|
def stop(self):
|
|
@@ -129,20 +164,24 @@ class VoiceManager:
|
|
|
129
164
|
try:
|
|
130
165
|
self._abstractvoice_manager.stop_speaking()
|
|
131
166
|
if self.debug_mode:
|
|
132
|
-
|
|
167
|
+
if self.debug_mode:
|
|
168
|
+
print("🔊 AbstractVoice speech stopped")
|
|
133
169
|
except Exception as e:
|
|
134
170
|
if self.debug_mode:
|
|
135
|
-
|
|
171
|
+
if self.debug_mode:
|
|
172
|
+
print(f"❌ Error stopping AbstractVoice: {e}")
|
|
136
173
|
|
|
137
174
|
def cleanup(self):
|
|
138
175
|
"""Clean up TTS resources."""
|
|
139
176
|
try:
|
|
140
177
|
self._abstractvoice_manager.cleanup()
|
|
141
178
|
if self.debug_mode:
|
|
142
|
-
|
|
179
|
+
if self.debug_mode:
|
|
180
|
+
print("🔊 AbstractVoice cleaned up")
|
|
143
181
|
except Exception as e:
|
|
144
182
|
if self.debug_mode:
|
|
145
|
-
|
|
183
|
+
if self.debug_mode:
|
|
184
|
+
print(f"❌ Error cleaning up AbstractVoice: {e}")
|
|
146
185
|
|
|
147
186
|
# STT (Speech-to-Text) Methods for Full Voice Mode
|
|
148
187
|
|
|
@@ -156,13 +195,16 @@ class VoiceManager:
|
|
|
156
195
|
try:
|
|
157
196
|
self._abstractvoice_manager.set_voice_mode(mode)
|
|
158
197
|
if self.debug_mode:
|
|
159
|
-
|
|
198
|
+
if self.debug_mode:
|
|
199
|
+
print(f"🔊 Voice mode set to: {mode}")
|
|
160
200
|
except Exception as e:
|
|
161
201
|
if self.debug_mode:
|
|
162
|
-
|
|
202
|
+
if self.debug_mode:
|
|
203
|
+
print(f"❌ Error setting voice mode: {e}")
|
|
163
204
|
else:
|
|
164
205
|
if self.debug_mode:
|
|
165
|
-
|
|
206
|
+
if self.debug_mode:
|
|
207
|
+
print(f"⚠️ Voice mode setting not available, simulating mode: {mode}")
|
|
166
208
|
|
|
167
209
|
def listen(self, on_transcription: Callable[[str], None], on_stop: Callable[[], None] = None):
|
|
168
210
|
"""Start listening for speech input.
|
|
@@ -178,14 +220,17 @@ class VoiceManager:
|
|
|
178
220
|
on_stop=on_stop
|
|
179
221
|
)
|
|
180
222
|
if self.debug_mode:
|
|
181
|
-
|
|
223
|
+
if self.debug_mode:
|
|
224
|
+
print("🎤 Started listening for speech")
|
|
182
225
|
except Exception as e:
|
|
183
226
|
if self.debug_mode:
|
|
184
|
-
|
|
227
|
+
if self.debug_mode:
|
|
228
|
+
print(f"❌ Error starting listening: {e}")
|
|
185
229
|
raise
|
|
186
230
|
else:
|
|
187
231
|
if self.debug_mode:
|
|
188
|
-
|
|
232
|
+
if self.debug_mode:
|
|
233
|
+
print("⚠️ STT listening not available in current AbstractVoice version")
|
|
189
234
|
raise RuntimeError("STT listening not available")
|
|
190
235
|
|
|
191
236
|
def stop_listening(self):
|
|
@@ -194,13 +239,16 @@ class VoiceManager:
|
|
|
194
239
|
try:
|
|
195
240
|
self._abstractvoice_manager.stop_listening()
|
|
196
241
|
if self.debug_mode:
|
|
197
|
-
|
|
242
|
+
if self.debug_mode:
|
|
243
|
+
print("🎤 Stopped listening for speech")
|
|
198
244
|
except Exception as e:
|
|
199
245
|
if self.debug_mode:
|
|
200
|
-
|
|
246
|
+
if self.debug_mode:
|
|
247
|
+
print(f"❌ Error stopping listening: {e}")
|
|
201
248
|
else:
|
|
202
249
|
if self.debug_mode:
|
|
203
|
-
|
|
250
|
+
if self.debug_mode:
|
|
251
|
+
print("⚠️ Stop listening not available in current AbstractVoice version")
|
|
204
252
|
|
|
205
253
|
def is_listening(self) -> bool:
|
|
206
254
|
"""Check if currently listening for speech."""
|
|
@@ -209,11 +257,13 @@ class VoiceManager:
|
|
|
209
257
|
return self._abstractvoice_manager.is_listening()
|
|
210
258
|
except Exception as e:
|
|
211
259
|
if self.debug_mode:
|
|
212
|
-
|
|
260
|
+
if self.debug_mode:
|
|
261
|
+
print(f"❌ Error checking listening state: {e}")
|
|
213
262
|
return False
|
|
214
263
|
else:
|
|
215
264
|
if self.debug_mode:
|
|
216
|
-
|
|
265
|
+
if self.debug_mode:
|
|
266
|
+
print("⚠️ Listening state check not available")
|
|
217
267
|
return False
|
|
218
268
|
|
|
219
269
|
|
|
@@ -240,7 +240,7 @@ class iPhoneMessagesDialog:
|
|
|
240
240
|
background: transparent;
|
|
241
241
|
border: none;
|
|
242
242
|
text-align: left;
|
|
243
|
-
font-family: "
|
|
243
|
+
font-family: "Helvetica Neue", "Helvetica", Arial, sans-serif;
|
|
244
244
|
}
|
|
245
245
|
""")
|
|
246
246
|
nav_layout.addWidget(back_btn)
|
|
@@ -254,7 +254,7 @@ class iPhoneMessagesDialog:
|
|
|
254
254
|
color: #ffffff;
|
|
255
255
|
font-size: 17px;
|
|
256
256
|
font-weight: 600;
|
|
257
|
-
font-family: "
|
|
257
|
+
font-family: "Helvetica Neue", "Helvetica", Arial, sans-serif;
|
|
258
258
|
}
|
|
259
259
|
""")
|
|
260
260
|
nav_layout.addWidget(title)
|
|
@@ -328,7 +328,7 @@ class iPhoneMessagesDialog:
|
|
|
328
328
|
font-size: 14px;
|
|
329
329
|
font-weight: 400;
|
|
330
330
|
line-height: 18px;
|
|
331
|
-
font-family: "
|
|
331
|
+
font-family: "Helvetica Neue", "Helvetica", Arial, sans-serif;
|
|
332
332
|
}
|
|
333
333
|
""")
|
|
334
334
|
# Right align
|
|
@@ -351,7 +351,7 @@ class iPhoneMessagesDialog:
|
|
|
351
351
|
font-size: 14px;
|
|
352
352
|
font-weight: 400;
|
|
353
353
|
line-height: 18px;
|
|
354
|
-
font-family: "
|
|
354
|
+
font-family: "Helvetica Neue", "Helvetica", Arial, sans-serif;
|
|
355
355
|
}
|
|
356
356
|
""")
|
|
357
357
|
# Left align
|
|
@@ -394,7 +394,7 @@ class iPhoneMessagesDialog:
|
|
|
394
394
|
font-size: 13px;
|
|
395
395
|
font-weight: 400;
|
|
396
396
|
color: rgba(255, 255, 255, 0.6);
|
|
397
|
-
font-family: "
|
|
397
|
+
font-family: "Helvetica Neue", "Helvetica", Arial, sans-serif;
|
|
398
398
|
padding: 0px;
|
|
399
399
|
}
|
|
400
400
|
""")
|