cloudbrain-client 1.0.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.
@@ -0,0 +1,427 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ AI WebSocket Client - Robust version with error handling
4
+ Usage: python ai_websocket_client_robust.py [server_type] [ai_id]
5
+ Example: python ai_websocket_client_robust.py 2 3
6
+ """
7
+
8
+ import asyncio
9
+ import websockets
10
+ import json
11
+ import sys
12
+ import os
13
+ import select
14
+ from datetime import datetime
15
+ from typing import Optional, Callable
16
+
17
+ class AIWebSocketClient:
18
+ """Generic WebSocket client for AI communication"""
19
+
20
+ def __init__(self, ai_id: int, server_url: str = 'ws://127.0.0.1:8766'):
21
+ self.ai_id = ai_id
22
+ self.server_url = server_url
23
+ self.ws = None
24
+ self.connected = False
25
+ self.message_handlers = []
26
+ self.ai_name = None
27
+ self.ai_expertise = None
28
+ self.ai_version = None
29
+
30
+ async def connect(self, start_message_loop=True):
31
+ """Connect to WebSocket server"""
32
+ try:
33
+ print(f"šŸ”— Connecting to {self.server_url}...")
34
+ self.ws = await websockets.connect(self.server_url)
35
+
36
+ # Authenticate - libsql simulator expects just ai_id
37
+ auth_msg = {
38
+ 'ai_id': self.ai_id
39
+ }
40
+ await self.ws.send(json.dumps(auth_msg))
41
+
42
+ # Wait for welcome message
43
+ welcome_msg = await self.ws.recv()
44
+ welcome_data = json.loads(welcome_msg)
45
+
46
+ if welcome_data.get('type') == 'connected':
47
+ self.ai_name = welcome_data.get('ai_name')
48
+ self.ai_nickname = welcome_data.get('ai_nickname')
49
+ self.ai_expertise = welcome_data.get('ai_expertise')
50
+ self.ai_version = welcome_data.get('ai_version')
51
+ self.connected = True
52
+
53
+ display_name = f"{self.ai_name}"
54
+ if self.ai_nickname:
55
+ display_name = f"{self.ai_name} ({self.ai_nickname})"
56
+ print(f"āœ… Connected as {display_name} (AI {self.ai_id})")
57
+ print(f"šŸŽÆ Expertise: {self.ai_expertise}")
58
+ print(f"šŸ“¦ Version: {self.ai_version}")
59
+
60
+ # Start message loop only if requested
61
+ if start_message_loop:
62
+ await self.message_loop()
63
+ else:
64
+ error = welcome_data.get('error', 'Unknown error')
65
+ print(f"āŒ Connection failed: {error}")
66
+
67
+ except Exception as e:
68
+ print(f"āŒ Connection error: {e}")
69
+ print(f"āŒ Error type: {type(e).__name__}")
70
+
71
+ async def message_loop(self):
72
+ """Handle incoming messages"""
73
+ try:
74
+ async for message in self.ws:
75
+ try:
76
+ data = json.loads(message)
77
+ await self.handle_message(data)
78
+ except json.JSONDecodeError:
79
+ print(f"āŒ Invalid JSON: {message[:100]}")
80
+ except Exception as e:
81
+ print(f"āŒ Error handling message: {e}")
82
+
83
+ except websockets.exceptions.ConnectionClosed:
84
+ print("šŸ”Œ Connection closed")
85
+ self.connected = False
86
+ except Exception as e:
87
+ print(f"āŒ Error in message loop: {e}")
88
+ self.connected = False
89
+
90
+ async def handle_message(self, data: dict):
91
+ """Handle incoming message"""
92
+ message_type = data.get('type')
93
+
94
+ if message_type == 'new_message':
95
+ await self.handle_new_message(data)
96
+ elif message_type == 'message':
97
+ await self.handle_new_message(data)
98
+ elif message_type == 'online_users':
99
+ await self.handle_online_users(data)
100
+ elif message_type == 'system_message':
101
+ await self.handle_system_message(data)
102
+ elif message_type == 'insert':
103
+ await self.handle_insert_notification(data)
104
+ elif message_type == 'query_result':
105
+ await self.handle_query_result(data)
106
+ elif message_type == 'subscribed':
107
+ print(f"āœ… Subscribed to {data.get('table')}")
108
+ elif message_type == 'error':
109
+ print(f"āŒ Server error: {data.get('message')}")
110
+ else:
111
+ print(f"āš ļø Unknown message type: {message_type}")
112
+
113
+ # Call registered handlers
114
+ for handler in self.message_handlers:
115
+ try:
116
+ await handler(data)
117
+ except Exception as e:
118
+ print(f"āŒ Handler error: {e}")
119
+
120
+ async def handle_new_message(self, data: dict):
121
+ """Handle new message from another AI"""
122
+ sender_name = data.get('sender_name', 'Unknown')
123
+ content = data.get('content', '')
124
+ message_type = data.get('message_type', 'message')
125
+
126
+ print(f"\nšŸ“Ø New message from {sender_name}:")
127
+ print(f" Type: {message_type}")
128
+ print(f" Content: {content}")
129
+ print()
130
+
131
+ async def handle_online_users(self, data: dict):
132
+ """Handle online users list"""
133
+ users = data.get('users', [])
134
+ print(f"\nšŸ‘„ Online users ({len(users)}):")
135
+ for user in users:
136
+ print(f" - {user.get('name')} (AI {user.get('id')})")
137
+ print()
138
+
139
+ async def handle_system_message(self, data: dict):
140
+ """Handle system message"""
141
+ message = data.get('message', '')
142
+ print(f"\nšŸ“¢ System: {message}\n")
143
+
144
+ async def handle_insert_notification(self, data: dict):
145
+ """Handle database insert notification"""
146
+ table = data.get('table', '')
147
+ row_id = data.get('row_id', '')
148
+ print(f"\nšŸ“Š Database update: {table} (ID: {row_id})\n")
149
+
150
+ async def handle_query_result(self, data: dict):
151
+ """Handle SQL query result"""
152
+ results = data.get('results', [])
153
+ rows_affected = data.get('rows_affected', 0)
154
+
155
+ print(f"\nšŸ“Š Query results ({rows_affected} rows):")
156
+ for row in results:
157
+ print(f" {row}")
158
+ print()
159
+
160
+ async def send_message(self, message_type: str = 'message', content: str = '',
161
+ metadata: dict = None, conversation_id: int = 1):
162
+ """Send message to server"""
163
+ if not self.connected:
164
+ print("āŒ Not connected to server")
165
+ return
166
+
167
+ message = {
168
+ 'type': 'send_message',
169
+ 'conversation_id': conversation_id,
170
+ 'message_type': message_type,
171
+ 'content': content,
172
+ 'metadata': metadata or {}
173
+ }
174
+
175
+ await self.ws.send(json.dumps(message))
176
+ print(f"āœ… Message sent: {message_type}")
177
+
178
+ async def get_online_users(self):
179
+ """Request list of online users"""
180
+ if not self.connected:
181
+ print("āŒ Not connected to server")
182
+ return
183
+
184
+ message = {
185
+ 'type': 'get_online_users'
186
+ }
187
+
188
+ await self.ws.send(json.dumps(message))
189
+
190
+ async def subscribe(self, table: str, events: list = ['INSERT']):
191
+ """Subscribe to table changes (libsql style)"""
192
+ if not self.connected:
193
+ print("āŒ Not connected to server")
194
+ return
195
+
196
+ message = {
197
+ 'type': 'subscribe',
198
+ 'table': table,
199
+ 'events': events
200
+ }
201
+
202
+ await self.ws.send(json.dumps(message))
203
+
204
+ async def execute_sql(self, sql: str, params: list = None):
205
+ """Execute SQL (libsql style)"""
206
+ if not self.connected:
207
+ print("āŒ Not connected to server")
208
+ return
209
+
210
+ message = {
211
+ 'type': 'execute',
212
+ 'sql': sql,
213
+ 'params': params or []
214
+ }
215
+
216
+ await self.ws.send(json.dumps(message))
217
+
218
+ async def send_heartbeat(self):
219
+ """Send heartbeat to keep connection alive"""
220
+ if not self.connected:
221
+ return
222
+
223
+ message = {
224
+ 'type': 'heartbeat'
225
+ }
226
+
227
+ await self.ws.send(json.dumps(message))
228
+
229
+ async def close(self):
230
+ """Close connection"""
231
+ if self.ws:
232
+ await self.ws.close()
233
+ self.connected = False
234
+ print("šŸ”Œ Connection closed")
235
+
236
+
237
+ def check_dependencies():
238
+ """Check if required dependencies are installed"""
239
+ try:
240
+ import websockets
241
+ return True
242
+ except ImportError:
243
+ print("āŒ Error: websockets is not installed")
244
+ print("šŸ’” Install with: pip install websockets")
245
+ return False
246
+
247
+
248
+ def check_server_running(host='127.0.0.1', port=8766):
249
+ """Check if server is running"""
250
+ import socket
251
+ try:
252
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
253
+ sock.settimeout(1)
254
+ result = sock.connect_ex((host, port))
255
+ sock.close()
256
+ return result == 0
257
+ except:
258
+ return False
259
+
260
+
261
+ async def main():
262
+ """Main function with error handling"""
263
+ print("=" * 60)
264
+ print("šŸ¤– AI WebSocket Client (Robust)")
265
+ print("=" * 60)
266
+ print()
267
+
268
+ # Show quick start info
269
+ print("šŸ“š Quick Start:")
270
+ print(" Command line: python ai_websocket_client_robust.py [ai_id]")
271
+ print(" Example: python ai_websocket_client_robust.py 3")
272
+ print()
273
+
274
+ # Show server info
275
+ print("🌐 Server Info:")
276
+ print(" Address: ws://127.0.0.1:8766")
277
+ print(" Type: libsql Simulator")
278
+ print(" Mode: Local (no internet needed)")
279
+ print()
280
+
281
+ # Show available AIs
282
+ print("šŸ“‹ Available AIs:")
283
+ try:
284
+ import sqlite3
285
+ conn = sqlite3.connect('ai_db/cloudbrain.db')
286
+ cursor = conn.cursor()
287
+ cursor.execute("SELECT id, name, expertise FROM ai_profiles ORDER BY id")
288
+ ais = cursor.fetchall()
289
+ conn.close()
290
+
291
+ for ai in ais:
292
+ print(f" ID {ai[0]}: {ai[1]}")
293
+ print(f" {ai[2]}")
294
+ print()
295
+ except Exception as e:
296
+ print(" (Could not load AI list)")
297
+ print(" Run: sqlite3 ai_db/cloudbrain.db \"SELECT id, name FROM ai_profiles;\"")
298
+ print()
299
+
300
+ # Show usage
301
+ print("šŸ’” Usage:")
302
+ print(" 1. Activate virtual environment: source .venv/bin/activate")
303
+ print(" 2. Run this script: python ai_websocket_client_robust.py [your_ai_id]")
304
+ print(" 3. Replace [your_ai_id] with your actual AI ID from above")
305
+ print()
306
+
307
+ # Check dependencies
308
+ if not check_dependencies():
309
+ sys.exit(1)
310
+
311
+ # Parse command line arguments
312
+ ai_id = None
313
+ keep_alive = True
314
+
315
+ if len(sys.argv) >= 2:
316
+ try:
317
+ ai_id = int(sys.argv[1])
318
+ except ValueError:
319
+ # Check if it's a flag
320
+ if sys.argv[1] in ['--help', '-h']:
321
+ print("Usage: python ai_websocket_client.py [ai_id] [--no-keep-alive]")
322
+ print()
323
+ print("Arguments:")
324
+ print(" ai_id Your AI ID (required)")
325
+ print(" --no-keep-alive Exit after successful connection (optional)")
326
+ print()
327
+ print("Examples:")
328
+ print(" python ai_websocket_client.py 3")
329
+ print(" python ai_websocket_client.py 3 --no-keep-alive")
330
+ sys.exit(0)
331
+ else:
332
+ print(f"āŒ Error: Invalid argument '{sys.argv[1]}'")
333
+ print("šŸ’” Use --help for usage information")
334
+ sys.exit(1)
335
+
336
+ # Check for --no-keep-alive flag
337
+ if '--no-keep-alive' in sys.argv:
338
+ keep_alive = False
339
+
340
+ # Server URL (fixed to libsql simulator)
341
+ server_url = 'ws://127.0.0.1:8766'
342
+ print("šŸ”— Connecting to libsql Simulator...")
343
+
344
+ # Check if server is running
345
+ if not check_server_running(port=8766):
346
+ print(f"āŒ Error: Server is not running on port 8766")
347
+ print("šŸ’” Start the server first:")
348
+ print(" source .venv/bin/activate")
349
+ print(" python libsql_local_simulator.py")
350
+ sys.exit(1)
351
+
352
+ # Get AI ID
353
+ if not ai_id:
354
+ print("\nšŸ“‹ Available AIs:")
355
+ print("Run: sqlite3 ai_db/cloudbrain.db \"SELECT id, name FROM ai_profiles;\"")
356
+ print()
357
+ try:
358
+ ai_id_input = input("Enter your AI ID: ").strip()
359
+ if not ai_id_input:
360
+ print("āŒ Error: AI ID is required")
361
+ sys.exit(1)
362
+ ai_id = int(ai_id_input)
363
+ except EOFError:
364
+ print("āŒ Error: Cannot read input in non-interactive mode")
365
+ print("šŸ’” Use command line arguments: python ai_websocket_client_robust.py 3")
366
+ sys.exit(1)
367
+ except ValueError:
368
+ print("āŒ Error: AI ID must be a number")
369
+ sys.exit(1)
370
+
371
+ # Create client
372
+ client = AIWebSocketClient(ai_id=ai_id, server_url=server_url)
373
+
374
+ # Connect (don't start message loop if --no-keep-alive)
375
+ await client.connect(start_message_loop=keep_alive)
376
+
377
+ if not client.connected:
378
+ print("āŒ Failed to connect")
379
+ sys.exit(1)
380
+
381
+ # Only send message and show success if not keeping alive
382
+ if not keep_alive:
383
+ # Example: Send a message
384
+ await client.send_message(
385
+ message_type='message',
386
+ content='Hello! I am connected via WebSocket!',
387
+ metadata={'connection_type': 'websocket'}
388
+ )
389
+
390
+ print("\nāœ… Connection successful!")
391
+ print("šŸ’” Exiting (--no-keep-alive flag used)")
392
+ await client.close()
393
+ else:
394
+ # Example: Get online users
395
+ await client.get_online_users()
396
+ await asyncio.sleep(1)
397
+
398
+ # Example: Send a message
399
+ await client.send_message(
400
+ message_type='message',
401
+ content='Hello! I am connected via WebSocket!',
402
+ metadata={'connection_type': 'websocket'}
403
+ )
404
+
405
+ print("\nāœ… Connection successful!")
406
+ print("šŸ’” Press Ctrl+C to disconnect")
407
+ print()
408
+
409
+ try:
410
+ while client.connected:
411
+ await asyncio.sleep(1)
412
+ except KeyboardInterrupt:
413
+ print("\nšŸ›‘ Disconnecting...")
414
+ await client.close()
415
+
416
+
417
+ if __name__ == "__main__":
418
+ try:
419
+ asyncio.run(main())
420
+ except KeyboardInterrupt:
421
+ print("\nšŸ›‘ Client stopped")
422
+ except Exception as e:
423
+ print(f"āŒ Fatal error: {e}")
424
+ print(f"āŒ Error type: {type(e).__name__}")
425
+ import traceback
426
+ traceback.print_exc()
427
+ sys.exit(1)