ciyidogan commited on
Commit
95e2fc0
·
verified ·
1 Parent(s): 84dee41

Delete stt_lifecycle_manager.py

Browse files
Files changed (1) hide show
  1. stt_lifecycle_manager.py +0 -368
stt_lifecycle_manager.py DELETED
@@ -1,368 +0,0 @@
1
- """
2
- STT Lifecycle Manager for Flare
3
- ===============================
4
- Manages STT instances lifecycle per session
5
- """
6
- import asyncio
7
- from typing import Dict, Optional, Any
8
- from datetime import datetime
9
- import traceback
10
- import base64
11
-
12
- from event_bus import EventBus, Event, EventType, publish_error
13
- from resource_manager import ResourceManager, ResourceType
14
- from stt.stt_factory import STTFactory
15
- from stt.stt_interface import STTInterface, STTConfig, TranscriptionResult
16
- from utils.logger import log_info, log_error, log_debug, log_warning
17
-
18
-
19
- class STTSession:
20
- """STT session wrapper"""
21
-
22
- def __init__(self, session_id: str, stt_instance: STTInterface):
23
- self.session_id = session_id
24
- self.stt_instance = stt_instance
25
- self.is_streaming = False
26
- self.config: Optional[STTConfig] = None
27
- self.created_at = datetime.utcnow()
28
- self.last_activity = datetime.utcnow()
29
- self.total_chunks = 0
30
- self.total_bytes = 0
31
-
32
- def update_activity(self):
33
- """Update last activity timestamp"""
34
- self.last_activity = datetime.utcnow()
35
-
36
-
37
- class STTLifecycleManager:
38
- """Manages STT instances lifecycle"""
39
-
40
- def __init__(self, event_bus: EventBus, resource_manager: ResourceManager):
41
- self.event_bus = event_bus
42
- self.resource_manager = resource_manager
43
- self.stt_sessions: Dict[str, STTSession] = {}
44
- self._setup_event_handlers()
45
- self._setup_resource_pool()
46
-
47
- def _setup_event_handlers(self):
48
- """Subscribe to STT-related events"""
49
- self.event_bus.subscribe(EventType.STT_STARTED, self._handle_stt_start)
50
- self.event_bus.subscribe(EventType.STT_STOPPED, self._handle_stt_stop)
51
- self.event_bus.subscribe(EventType.AUDIO_CHUNK_RECEIVED, self._handle_audio_chunk)
52
- self.event_bus.subscribe(EventType.SESSION_ENDED, self._handle_session_ended)
53
-
54
- def _setup_resource_pool(self):
55
- """Setup STT instance pool"""
56
- self.resource_manager.register_pool(
57
- resource_type=ResourceType.STT_INSTANCE,
58
- factory=self._create_stt_instance,
59
- max_idle=5,
60
- max_age_seconds=300 # 5 minutes
61
- )
62
-
63
- async def _create_stt_instance(self) -> STTInterface:
64
- """Factory for creating STT instances"""
65
- try:
66
- stt_instance = STTFactory.create_provider()
67
- if not stt_instance:
68
- raise ValueError("Failed to create STT instance")
69
-
70
- log_debug("🎤 Created new STT instance")
71
- return stt_instance
72
-
73
- except Exception as e:
74
- log_error(f"❌ Failed to create STT instance", error=str(e))
75
- raise
76
-
77
- async def _handle_stt_start(self, event: Event):
78
- """Handle STT start request"""
79
- session_id = event.session_id
80
- config_data = event.data
81
-
82
- try:
83
- log_info(f"🎤 Starting STT", session_id=session_id)
84
-
85
- # Check if already exists
86
- if session_id in self.stt_sessions:
87
- stt_session = self.stt_sessions[session_id]
88
- if stt_session.is_streaming:
89
- log_warning(f"⚠️ STT already streaming", session_id=session_id)
90
- return
91
- else:
92
- # Acquire STT instance from pool
93
- resource_id = f"stt_{session_id}"
94
- stt_instance = await self.resource_manager.acquire(
95
- resource_id=resource_id,
96
- session_id=session_id,
97
- resource_type=ResourceType.STT_INSTANCE,
98
- cleanup_callback=self._cleanup_stt_instance
99
- )
100
-
101
- # Create session wrapper
102
- stt_session = STTSession(session_id, stt_instance)
103
- self.stt_sessions[session_id] = stt_session
104
-
105
- # Get session locale from state orchestrator
106
- locale = config_data.get("locale", "tr")
107
-
108
- # Build STT config - ✅ CONTINUOUS LISTENING İÇİN AYARLAR
109
- stt_config = STTConfig(
110
- language=self._get_language_code(locale),
111
- sample_rate=config_data.get("sample_rate", 16000),
112
- encoding=config_data.get("encoding", "WEBM_OPUS"), # Try "LINEAR16" if WEBM fails
113
- enable_punctuation=config_data.get("enable_punctuation", True),
114
- enable_word_timestamps=False,
115
- model=config_data.get("model", "latest_long"),
116
- use_enhanced=config_data.get("use_enhanced", True),
117
- single_utterance=False, # ✅ Continuous listening için FALSE olmalı
118
- interim_results=True, # ✅ Interim results'ı AÇ
119
- )
120
-
121
- # Log the exact config being used
122
- log_info(f"📋 STT Config: encoding={stt_config.encoding}, "
123
- f"sample_rate={stt_config.sample_rate}, "
124
- f"single_utterance={stt_config.single_utterance}, "
125
- f"interim_results={stt_config.interim_results}")
126
-
127
- stt_session.config = stt_config
128
-
129
- # Start streaming
130
- await stt_session.stt_instance.start_streaming(stt_config)
131
- stt_session.is_streaming = True
132
- stt_session.update_activity()
133
-
134
- log_info(f"✅ STT started in continuous mode with interim results", session_id=session_id, language=stt_config.language)
135
-
136
- # Notify STT is ready
137
- await self.event_bus.publish(Event(
138
- type=EventType.STT_READY,
139
- session_id=session_id,
140
- data={"language": stt_config.language}
141
- ))
142
-
143
- except Exception as e:
144
- log_error(
145
- f"❌ Failed to start STT",
146
- session_id=session_id,
147
- error=str(e),
148
- traceback=traceback.format_exc()
149
- )
150
-
151
- # Clean up on error
152
- if session_id in self.stt_sessions:
153
- await self._cleanup_session(session_id)
154
-
155
- # Publish error event
156
- await publish_error(
157
- session_id=session_id,
158
- error_type="stt_error",
159
- error_message=f"Failed to start STT: {str(e)}"
160
- )
161
-
162
- async def _handle_stt_stop(self, event: Event):
163
- """Handle STT stop request"""
164
- session_id = event.session_id
165
- reason = event.data.get("reason", "unknown")
166
-
167
- log_info(f"🛑 Stopping STT", session_id=session_id, reason=reason)
168
-
169
- stt_session = self.stt_sessions.get(session_id)
170
- if not stt_session:
171
- log_warning(f"⚠️ No STT session found", session_id=session_id)
172
- return
173
-
174
- try:
175
- if stt_session.is_streaming:
176
- # Stop streaming
177
- final_result = await stt_session.stt_instance.stop_streaming()
178
- stt_session.is_streaming = False
179
-
180
- # If we got a final result, publish it
181
- if final_result and final_result.text:
182
- await self.event_bus.publish(Event(
183
- type=EventType.STT_RESULT,
184
- session_id=session_id,
185
- data={
186
- "text": final_result.text,
187
- "is_final": True,
188
- "confidence": final_result.confidence
189
- }
190
- ))
191
-
192
- # Don't remove session immediately - might restart
193
- stt_session.update_activity()
194
-
195
- log_info(f"✅ STT stopped", session_id=session_id)
196
-
197
- except Exception as e:
198
- log_error(
199
- f"❌ Error stopping STT",
200
- session_id=session_id,
201
- error=str(e)
202
- )
203
-
204
- async def _handle_audio_chunk(self, event: Event):
205
- """Process audio chunk through STT"""
206
- session_id = event.session_id
207
-
208
- stt_session = self.stt_sessions.get(session_id)
209
- if not stt_session or not stt_session.is_streaming:
210
- # STT not ready, ignore chunk
211
- return
212
-
213
- try:
214
- # Decode audio data
215
- audio_data = base64.b64decode(event.data.get("audio_data", ""))
216
-
217
- # Update stats
218
- stt_session.total_chunks += 1
219
- stt_session.total_bytes += len(audio_data)
220
- stt_session.update_activity()
221
-
222
- # Stream to STT
223
- async for result in stt_session.stt_instance.stream_audio(audio_data):
224
- # Publish transcription results
225
- await self.event_bus.publish(Event(
226
- type=EventType.STT_RESULT,
227
- session_id=session_id,
228
- data={
229
- "text": result.text,
230
- "is_final": result.is_final,
231
- "confidence": result.confidence,
232
- "timestamp": result.timestamp
233
- }
234
- ))
235
-
236
- # Log final results
237
- if result.is_final:
238
- log_info(
239
- f"📝 STT final result",
240
- session_id=session_id,
241
- text=result.text[:50] + "..." if len(result.text) > 50 else result.text,
242
- confidence=result.confidence
243
- )
244
-
245
- # Log progress periodically
246
- if stt_session.total_chunks % 100 == 0:
247
- log_debug(
248
- f"📊 STT progress",
249
- session_id=session_id,
250
- chunks=stt_session.total_chunks,
251
- bytes=stt_session.total_bytes
252
- )
253
-
254
- except Exception as e:
255
- log_error(
256
- f"❌ Error processing audio chunk",
257
- session_id=session_id,
258
- error=str(e)
259
- )
260
-
261
- # Check if it's a recoverable error
262
- if "stream duration" in str(e) or "timeout" in str(e).lower():
263
- # STT timeout, restart needed
264
- await publish_error(
265
- session_id=session_id,
266
- error_type="stt_timeout",
267
- error_message="STT stream timeout, restart needed"
268
- )
269
- else:
270
- # Other STT error
271
- await publish_error(
272
- session_id=session_id,
273
- error_type="stt_error",
274
- error_message=str(e)
275
- )
276
-
277
- async def _handle_session_ended(self, event: Event):
278
- """Clean up STT resources when session ends"""
279
- session_id = event.session_id
280
- await self._cleanup_session(session_id)
281
-
282
- async def _cleanup_session(self, session_id: str):
283
- """Clean up STT session"""
284
- stt_session = self.stt_sessions.pop(session_id, None)
285
- if not stt_session:
286
- return
287
-
288
- try:
289
- # Stop streaming if active
290
- if stt_session.is_streaming:
291
- await stt_session.stt_instance.stop_streaming()
292
-
293
- # Release resource
294
- resource_id = f"stt_{session_id}"
295
- await self.resource_manager.release(resource_id, delay_seconds=60)
296
-
297
- log_info(
298
- f"🧹 STT session cleaned up",
299
- session_id=session_id,
300
- total_chunks=stt_session.total_chunks,
301
- total_bytes=stt_session.total_bytes
302
- )
303
-
304
- except Exception as e:
305
- log_error(
306
- f"❌ Error cleaning up STT session",
307
- session_id=session_id,
308
- error=str(e)
309
- )
310
-
311
- async def _cleanup_stt_instance(self, stt_instance: STTInterface):
312
- """Cleanup callback for STT instance"""
313
- try:
314
- # Ensure streaming is stopped
315
- if hasattr(stt_instance, 'is_streaming') and stt_instance.is_streaming:
316
- await stt_instance.stop_streaming()
317
-
318
- log_debug("🧹 STT instance cleaned up")
319
-
320
- except Exception as e:
321
- log_error(f"❌ Error cleaning up STT instance", error=str(e))
322
-
323
- def _get_language_code(self, locale: str) -> str:
324
- """Convert locale to STT language code"""
325
- # Map common locales to STT language codes
326
- locale_map = {
327
- "tr": "tr-TR",
328
- "en": "en-US",
329
- "de": "de-DE",
330
- "fr": "fr-FR",
331
- "es": "es-ES",
332
- "it": "it-IT",
333
- "pt": "pt-BR",
334
- "ru": "ru-RU",
335
- "ja": "ja-JP",
336
- "ko": "ko-KR",
337
- "zh": "zh-CN",
338
- "ar": "ar-SA"
339
- }
340
-
341
- # Check direct match
342
- if locale in locale_map:
343
- return locale_map[locale]
344
-
345
- # Check if it's already a full code
346
- if "-" in locale and len(locale) == 5:
347
- return locale
348
-
349
- # Default to locale-LOCALE format
350
- return f"{locale}-{locale.upper()}"
351
-
352
- def get_stats(self) -> Dict[str, Any]:
353
- """Get STT manager statistics"""
354
- session_stats = {}
355
- for session_id, stt_session in self.stt_sessions.items():
356
- session_stats[session_id] = {
357
- "is_streaming": stt_session.is_streaming,
358
- "total_chunks": stt_session.total_chunks,
359
- "total_bytes": stt_session.total_bytes,
360
- "uptime_seconds": (datetime.utcnow() - stt_session.created_at).total_seconds(),
361
- "last_activity": stt_session.last_activity.isoformat()
362
- }
363
-
364
- return {
365
- "active_sessions": len(self.stt_sessions),
366
- "streaming_sessions": sum(1 for s in self.stt_sessions.values() if s.is_streaming),
367
- "sessions": session_stats
368
- }