Peter Michael Gits Claude commited on
Commit
19396c4
Β·
1 Parent(s): 08815de

feat: Update version to v0.5.5 with environment variable support

Browse files

- Version bump to match main repository
- Prepare for WebSocket-only service mode deployment
- Add WebSocket endpoint validation tests

πŸ€– Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

version.py CHANGED
@@ -2,8 +2,8 @@
2
  Version information for ChatCal Voice-Enabled AI Assistant
3
  """
4
 
5
- __version__ = "0.5.4"
6
- __build_date__ = "2025-08-20T17:40:00"
7
  __description__ = "Voice-Enabled ChatCal AI Assistant with Hugging Face deployment"
8
 
9
  def get_version_info():
 
2
  Version information for ChatCal Voice-Enabled AI Assistant
3
  """
4
 
5
+ __version__ = "0.5.5"
6
+ __build_date__ = "2025-08-20T12:11:00"
7
  __description__ = "Voice-Enabled ChatCal AI Assistant with Hugging Face deployment"
8
 
9
  def get_version_info():
webrtc/tests/test_websocket_endpoints.py ADDED
@@ -0,0 +1,316 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Direct WebSocket endpoint validation for STT and TTS services
4
+ Tests each service independently to verify WebSocket functionality
5
+ """
6
+
7
+ import asyncio
8
+ import websockets
9
+ import json
10
+ import base64
11
+ import logging
12
+ import sys
13
+ from datetime import datetime
14
+
15
+ # Configure logging
16
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
17
+ logger = logging.getLogger(__name__)
18
+
19
+ # Service URLs
20
+ STT_WEBSOCKET_URL = "wss://pgits-stt-gpu-service.hf.space/ws/stt"
21
+ TTS_WEBSOCKET_URL = "wss://pgits-tts-gpu-service.hf.space/ws/tts"
22
+
23
+ class WebSocketTester:
24
+ """Direct WebSocket endpoint tester"""
25
+
26
+ def __init__(self):
27
+ self.test_results = {}
28
+
29
+ def create_test_audio_data(self):
30
+ """Create minimal test audio data"""
31
+ # Create a simple test audio blob (simulating WebM format)
32
+ test_data = b'webm_test_audio_data_' + b'0' * 1000 # 1KB test data
33
+ return test_data
34
+
35
+ async def test_stt_websocket(self):
36
+ """Test STT WebSocket endpoint directly"""
37
+ logger.info("🎀 Testing STT WebSocket endpoint...")
38
+
39
+ try:
40
+ logger.info(f"Connecting to: {STT_WEBSOCKET_URL}")
41
+
42
+ # Test connection with timeout
43
+ async with websockets.connect(STT_WEBSOCKET_URL, timeout=10) as websocket:
44
+ logger.info("βœ… STT WebSocket connection established")
45
+
46
+ # Wait for connection confirmation
47
+ try:
48
+ confirmation = await asyncio.wait_for(websocket.recv(), timeout=15)
49
+ confirmation_data = json.loads(confirmation)
50
+ logger.info(f"πŸ“¨ STT confirmation received: {confirmation_data}")
51
+
52
+ if confirmation_data.get("type") == "stt_connection_confirmed":
53
+ logger.info("βœ… STT connection confirmed properly")
54
+
55
+ # Send test audio
56
+ test_audio = self.create_test_audio_data()
57
+ audio_b64 = base64.b64encode(test_audio).decode('utf-8')
58
+
59
+ message = {
60
+ "type": "stt_audio_chunk",
61
+ "audio_data": audio_b64,
62
+ "language": "auto",
63
+ "model_size": "base"
64
+ }
65
+
66
+ logger.info("πŸ“€ Sending test audio to STT...")
67
+ await websocket.send(json.dumps(message))
68
+
69
+ # Wait for transcription
70
+ response = await asyncio.wait_for(websocket.recv(), timeout=30)
71
+ response_data = json.loads(response)
72
+
73
+ logger.info(f"πŸ“¨ STT response: {response_data}")
74
+
75
+ if response_data.get("type") == "stt_transcription":
76
+ transcription = response_data.get("text", "")
77
+ logger.info(f"βœ… STT transcription received: {transcription}")
78
+ self.test_results["stt"] = {"success": True, "transcription": transcription}
79
+ return True
80
+ elif response_data.get("type") == "stt_error":
81
+ error_msg = response_data.get("message", "Unknown error")
82
+ logger.error(f"❌ STT service error: {error_msg}")
83
+ self.test_results["stt"] = {"success": False, "error": error_msg}
84
+ return False
85
+ else:
86
+ logger.warning(f"⚠️ Unexpected STT response type: {response_data}")
87
+ self.test_results["stt"] = {"success": False, "error": f"Unexpected response: {response_data}"}
88
+ return False
89
+ else:
90
+ logger.error(f"❌ Invalid STT confirmation: {confirmation_data}")
91
+ self.test_results["stt"] = {"success": False, "error": f"Invalid confirmation: {confirmation_data}"}
92
+ return False
93
+
94
+ except asyncio.TimeoutError:
95
+ logger.error("❌ STT confirmation timeout")
96
+ self.test_results["stt"] = {"success": False, "error": "Confirmation timeout"}
97
+ return False
98
+
99
+ except websockets.exceptions.InvalidStatusCode as e:
100
+ logger.error(f"❌ STT WebSocket invalid status: {e}")
101
+ self.test_results["stt"] = {"success": False, "error": f"Invalid status: {e}"}
102
+ return False
103
+ except websockets.exceptions.WebSocketException as e:
104
+ logger.error(f"❌ STT WebSocket error: {e}")
105
+ self.test_results["stt"] = {"success": False, "error": f"WebSocket error: {e}"}
106
+ return False
107
+ except Exception as e:
108
+ logger.error(f"❌ STT test failed: {e}")
109
+ self.test_results["stt"] = {"success": False, "error": str(e)}
110
+ return False
111
+
112
+ async def test_tts_websocket(self):
113
+ """Test TTS WebSocket endpoint directly"""
114
+ logger.info("πŸ”Š Testing TTS WebSocket endpoint...")
115
+
116
+ try:
117
+ logger.info(f"Connecting to: {TTS_WEBSOCKET_URL}")
118
+
119
+ # Test connection with timeout
120
+ async with websockets.connect(TTS_WEBSOCKET_URL, timeout=10) as websocket:
121
+ logger.info("βœ… TTS WebSocket connection established")
122
+
123
+ # Wait for connection confirmation
124
+ try:
125
+ confirmation = await asyncio.wait_for(websocket.recv(), timeout=15)
126
+ confirmation_data = json.loads(confirmation)
127
+ logger.info(f"πŸ“¨ TTS confirmation received: {confirmation_data}")
128
+
129
+ if confirmation_data.get("type") == "tts_connection_confirmed":
130
+ logger.info("βœ… TTS connection confirmed properly")
131
+
132
+ # Send test text
133
+ test_text = "Hello, this is a WebSocket test of the text to speech service."
134
+
135
+ message = {
136
+ "type": "tts_synthesize",
137
+ "text": test_text,
138
+ "voice_preset": "v2/en_speaker_6"
139
+ }
140
+
141
+ logger.info(f"πŸ“€ Sending test text to TTS: {test_text}")
142
+ await websocket.send(json.dumps(message))
143
+
144
+ # Wait for audio response (TTS takes longer)
145
+ response = await asyncio.wait_for(websocket.recv(), timeout=60)
146
+ response_data = json.loads(response)
147
+
148
+ logger.info(f"πŸ“¨ TTS response type: {response_data.get('type')}")
149
+
150
+ if response_data.get("type") == "tts_audio_response":
151
+ audio_size = response_data.get("audio_size", 0)
152
+ logger.info(f"βœ… TTS audio generated: {audio_size} bytes")
153
+ self.test_results["tts"] = {"success": True, "audio_size": audio_size}
154
+ return True
155
+ elif response_data.get("type") == "tts_error":
156
+ error_msg = response_data.get("message", "Unknown error")
157
+ logger.error(f"❌ TTS service error: {error_msg}")
158
+ self.test_results["tts"] = {"success": False, "error": error_msg}
159
+ return False
160
+ else:
161
+ logger.warning(f"⚠️ Unexpected TTS response type: {response_data}")
162
+ self.test_results["tts"] = {"success": False, "error": f"Unexpected response: {response_data}"}
163
+ return False
164
+ else:
165
+ logger.error(f"❌ Invalid TTS confirmation: {confirmation_data}")
166
+ self.test_results["tts"] = {"success": False, "error": f"Invalid confirmation: {confirmation_data}"}
167
+ return False
168
+
169
+ except asyncio.TimeoutError:
170
+ logger.error("❌ TTS confirmation timeout")
171
+ self.test_results["tts"] = {"success": False, "error": "Confirmation timeout"}
172
+ return False
173
+
174
+ except websockets.exceptions.InvalidStatusCode as e:
175
+ logger.error(f"❌ TTS WebSocket invalid status: {e}")
176
+ self.test_results["tts"] = {"success": False, "error": f"Invalid status: {e}"}
177
+ return False
178
+ except websockets.exceptions.WebSocketException as e:
179
+ logger.error(f"❌ TTS WebSocket error: {e}")
180
+ self.test_results["tts"] = {"success": False, "error": f"WebSocket error: {e}"}
181
+ return False
182
+ except Exception as e:
183
+ logger.error(f"❌ TTS test failed: {e}")
184
+ self.test_results["tts"] = {"success": False, "error": str(e)}
185
+ return False
186
+
187
+ async def test_endpoint_availability(self):
188
+ """Test if WebSocket endpoints are even available"""
189
+ logger.info("πŸ” Testing endpoint availability...")
190
+
191
+ # Test STT endpoint
192
+ try:
193
+ logger.info(f"Testing connection to: {STT_WEBSOCKET_URL}")
194
+ async with websockets.connect(STT_WEBSOCKET_URL, timeout=5) as ws:
195
+ logger.info("βœ… STT endpoint is reachable")
196
+ self.test_results["stt_reachable"] = True
197
+ except Exception as e:
198
+ logger.error(f"❌ STT endpoint not reachable: {e}")
199
+ self.test_results["stt_reachable"] = False
200
+
201
+ # Test TTS endpoint
202
+ try:
203
+ logger.info(f"Testing connection to: {TTS_WEBSOCKET_URL}")
204
+ async with websockets.connect(TTS_WEBSOCKET_URL, timeout=5) as ws:
205
+ logger.info("βœ… TTS endpoint is reachable")
206
+ self.test_results["tts_reachable"] = True
207
+ except Exception as e:
208
+ logger.error(f"❌ TTS endpoint not reachable: {e}")
209
+ self.test_results["tts_reachable"] = False
210
+
211
+ async def run_all_tests(self):
212
+ """Run comprehensive WebSocket endpoint validation"""
213
+ logger.info("πŸš€ Starting WebSocket endpoint validation...")
214
+ logger.info(f"Test started at: {datetime.now().isoformat()}")
215
+
216
+ # Test basic endpoint availability first
217
+ await self.test_endpoint_availability()
218
+
219
+ # Test STT WebSocket functionality
220
+ stt_success = False
221
+ if self.test_results.get("stt_reachable"):
222
+ stt_success = await self.test_stt_websocket()
223
+ else:
224
+ logger.warning("⚠️ Skipping STT functional test - endpoint not reachable")
225
+
226
+ # Brief pause
227
+ await asyncio.sleep(2)
228
+
229
+ # Test TTS WebSocket functionality
230
+ tts_success = False
231
+ if self.test_results.get("tts_reachable"):
232
+ tts_success = await self.test_tts_websocket()
233
+ else:
234
+ logger.warning("⚠️ Skipping TTS functional test - endpoint not reachable")
235
+
236
+ # Print comprehensive results
237
+ self.print_test_results()
238
+
239
+ return stt_success and tts_success
240
+
241
+ def print_test_results(self):
242
+ """Print detailed test results"""
243
+ logger.info("\n" + "="*70)
244
+ logger.info("πŸ“Š WEBSOCKET ENDPOINT VALIDATION RESULTS")
245
+ logger.info("="*70)
246
+
247
+ # STT Results
248
+ logger.info("🎀 STT Service:")
249
+ logger.info(f" Endpoint Reachable: {'βœ…' if self.test_results.get('stt_reachable') else '❌'}")
250
+ if "stt" in self.test_results:
251
+ stt_result = self.test_results["stt"]
252
+ if stt_result["success"]:
253
+ logger.info(f" WebSocket Function: βœ… PASS")
254
+ logger.info(f" Transcription: {stt_result.get('transcription', 'N/A')}")
255
+ else:
256
+ logger.info(f" WebSocket Function: ❌ FAIL")
257
+ logger.info(f" Error: {stt_result.get('error', 'Unknown')}")
258
+ else:
259
+ logger.info(" WebSocket Function: ⚠️ NOT TESTED")
260
+
261
+ # TTS Results
262
+ logger.info("\nπŸ”Š TTS Service:")
263
+ logger.info(f" Endpoint Reachable: {'βœ…' if self.test_results.get('tts_reachable') else '❌'}")
264
+ if "tts" in self.test_results:
265
+ tts_result = self.test_results["tts"]
266
+ if tts_result["success"]:
267
+ logger.info(f" WebSocket Function: βœ… PASS")
268
+ logger.info(f" Audio Generated: {tts_result.get('audio_size', 0)} bytes")
269
+ else:
270
+ logger.info(f" WebSocket Function: ❌ FAIL")
271
+ logger.info(f" Error: {tts_result.get('error', 'Unknown')}")
272
+ else:
273
+ logger.info(" WebSocket Function: ⚠️ NOT TESTED")
274
+
275
+ logger.info("="*70)
276
+
277
+ # Overall status
278
+ stt_ok = self.test_results.get("stt_reachable") and self.test_results.get("stt", {}).get("success", False)
279
+ tts_ok = self.test_results.get("tts_reachable") and self.test_results.get("tts", {}).get("success", False)
280
+
281
+ if stt_ok and tts_ok:
282
+ logger.info("πŸŽ‰ ALL WEBSOCKET ENDPOINTS WORKING!")
283
+ logger.info("βœ… Ready for ChatCal WebRTC integration")
284
+ elif stt_ok or tts_ok:
285
+ logger.warning("⚠️ PARTIAL SUCCESS - Some endpoints working")
286
+ if not stt_ok:
287
+ logger.warning("❌ STT WebSocket needs attention")
288
+ if not tts_ok:
289
+ logger.warning("❌ TTS WebSocket needs attention")
290
+ else:
291
+ logger.error("❌ NO WEBSOCKET ENDPOINTS WORKING")
292
+ logger.error("πŸ”§ Services need WebSocket endpoint deployment")
293
+
294
+ logger.info(f"πŸ•’ Test completed at: {datetime.now().isoformat()}")
295
+
296
+ async def main():
297
+ """Main test runner"""
298
+ tester = WebSocketTester()
299
+
300
+ try:
301
+ success = await tester.run_all_tests()
302
+ return 0 if success else 1
303
+ except KeyboardInterrupt:
304
+ logger.info("❌ Tests interrupted by user")
305
+ return 1
306
+ except Exception as e:
307
+ logger.error(f"❌ Test runner failed: {e}")
308
+ return 1
309
+
310
+ if __name__ == "__main__":
311
+ try:
312
+ exit_code = asyncio.run(main())
313
+ sys.exit(exit_code)
314
+ except Exception as e:
315
+ logger.error(f"❌ Failed to run tests: {e}")
316
+ sys.exit(1)