unaiverse 0.1.6__cp313-cp313-musllinux_1_2_aarch64.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.
Potentially problematic release.
This version of unaiverse might be problematic. Click here for more details.
- unaiverse/__init__.py +19 -0
- unaiverse/agent.py +2008 -0
- unaiverse/agent_basics.py +1846 -0
- unaiverse/clock.py +191 -0
- unaiverse/dataprops.py +1209 -0
- unaiverse/hsm.py +1880 -0
- unaiverse/modules/__init__.py +18 -0
- unaiverse/modules/cnu/__init__.py +17 -0
- unaiverse/modules/cnu/cnus.py +536 -0
- unaiverse/modules/cnu/layers.py +261 -0
- unaiverse/modules/cnu/psi.py +60 -0
- unaiverse/modules/hl/__init__.py +15 -0
- unaiverse/modules/hl/hl_utils.py +411 -0
- unaiverse/modules/networks.py +1509 -0
- unaiverse/modules/utils.py +680 -0
- unaiverse/networking/__init__.py +16 -0
- unaiverse/networking/node/__init__.py +18 -0
- unaiverse/networking/node/connpool.py +1261 -0
- unaiverse/networking/node/node.py +2223 -0
- unaiverse/networking/node/profile.py +446 -0
- unaiverse/networking/node/tokens.py +79 -0
- unaiverse/networking/p2p/__init__.py +198 -0
- unaiverse/networking/p2p/go.mod +127 -0
- unaiverse/networking/p2p/go.sum +548 -0
- unaiverse/networking/p2p/golibp2p.py +18 -0
- unaiverse/networking/p2p/golibp2p.pyi +135 -0
- unaiverse/networking/p2p/lib.go +2714 -0
- unaiverse/networking/p2p/lib.go.sha256 +1 -0
- unaiverse/networking/p2p/lib_types.py +312 -0
- unaiverse/networking/p2p/message_pb2.py +63 -0
- unaiverse/networking/p2p/messages.py +265 -0
- unaiverse/networking/p2p/mylogger.py +77 -0
- unaiverse/networking/p2p/p2p.py +929 -0
- unaiverse/networking/p2p/proto-go/message.pb.go +616 -0
- unaiverse/networking/p2p/unailib.cpython-313-aarch64-linux-musl.so +0 -0
- unaiverse/streamlib/__init__.py +15 -0
- unaiverse/streamlib/streamlib.py +210 -0
- unaiverse/streams.py +770 -0
- unaiverse/utils/__init__.py +16 -0
- unaiverse/utils/ask_lone_wolf.json +27 -0
- unaiverse/utils/lone_wolf.json +19 -0
- unaiverse/utils/misc.py +305 -0
- unaiverse/utils/sandbox.py +293 -0
- unaiverse/utils/server.py +435 -0
- unaiverse/world.py +175 -0
- unaiverse-0.1.6.dist-info/METADATA +365 -0
- unaiverse-0.1.6.dist-info/RECORD +50 -0
- unaiverse-0.1.6.dist-info/WHEEL +5 -0
- unaiverse-0.1.6.dist-info/licenses/LICENSE +43 -0
- unaiverse-0.1.6.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
9715da07770e39746f5889cc25ef308badd9a48e82cd5b15cb98cad24213da89
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
"""
|
|
2
|
+
█████ █████ ██████ █████ █████ █████ █████ ██████████ ███████████ █████████ ██████████
|
|
3
|
+
░░███ ░░███ ░░██████ ░░███ ░░███ ░░███ ░░███ ░░███░░░░░█░░███░░░░░███ ███░░░░░███░░███░░░░░█
|
|
4
|
+
░███ ░███ ░███░███ ░███ ██████ ░███ ░███ ░███ ░███ █ ░ ░███ ░███ ░███ ░░░ ░███ █ ░
|
|
5
|
+
░███ ░███ ░███░░███░███ ░░░░░███ ░███ ░███ ░███ ░██████ ░██████████ ░░█████████ ░██████
|
|
6
|
+
░███ ░███ ░███ ░░██████ ███████ ░███ ░░███ ███ ░███░░█ ░███░░░░░███ ░░░░░░░░███ ░███░░█
|
|
7
|
+
░███ ░███ ░███ ░░█████ ███░░███ ░███ ░░░█████░ ░███ ░ █ ░███ ░███ ███ ░███ ░███ ░ █
|
|
8
|
+
░░████████ █████ ░░█████░░████████ █████ ░░███ ██████████ █████ █████░░█████████ ██████████
|
|
9
|
+
░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░ ░░░░░ ░░░ ░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░ ░░░░░░░░░░
|
|
10
|
+
A Collectionless AI Project (https://collectionless.ai)
|
|
11
|
+
Registration/Login: https://unaiverse.io
|
|
12
|
+
Code Repositories: https://github.com/collectionlessai/
|
|
13
|
+
Main Developers: Stefano Melacci (Project Leader), Christian Di Maio, Tommaso Guidi
|
|
14
|
+
"""
|
|
15
|
+
import json
|
|
16
|
+
import ctypes
|
|
17
|
+
import logging
|
|
18
|
+
from threading import Lock
|
|
19
|
+
from typing import Optional, List, Any, TYPE_CHECKING
|
|
20
|
+
|
|
21
|
+
from .golibp2p import GoLibP2P # Assuming this class loads the library
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger('LIB-TYPES')
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class TypeInterface:
|
|
27
|
+
"""
|
|
28
|
+
Helper class for converting between Python types and Go types using ctypes.
|
|
29
|
+
"""
|
|
30
|
+
def __init__(self, libp2p_instance: GoLibP2P):
|
|
31
|
+
self.__freed_pointers: set[int] = set() # Track freed pointers to prevent double-free errors
|
|
32
|
+
self.__freed_pointers_lock: Any = Lock() # A threading lock
|
|
33
|
+
self.libp2p: GoLibP2P = libp2p_instance # Store the shared library object instance
|
|
34
|
+
|
|
35
|
+
def to_go_string(self, s: str) -> bytes:
|
|
36
|
+
"""
|
|
37
|
+
Converts a Python string to a UTF-8 encoded Python 'bytes' object.
|
|
38
|
+
|
|
39
|
+
This 'bytes' object is suitable for direct use with ctypes when passing
|
|
40
|
+
to a C function expecting a 'char*' (ctypes.c_char_p), as ctypes
|
|
41
|
+
will automatically pass a pointer to the byte string's data.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
s: The Python string.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
A Python 'bytes' object containing the UTF-8 encoded string.
|
|
48
|
+
"""
|
|
49
|
+
if s is None:
|
|
50
|
+
s = ""
|
|
51
|
+
return s.encode("utf-8")
|
|
52
|
+
|
|
53
|
+
def from_go_string(self, cstr: bytes) -> str:
|
|
54
|
+
"""
|
|
55
|
+
Converts a C char pointer (Go string) to a Python string.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
cstr: The C char pointer.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
The decoded Python string.
|
|
62
|
+
"""
|
|
63
|
+
if not cstr:
|
|
64
|
+
return ""
|
|
65
|
+
return cstr.decode("utf-8")
|
|
66
|
+
|
|
67
|
+
def to_go_int(self, i: int) -> ctypes.c_int:
|
|
68
|
+
"""
|
|
69
|
+
Converts a Python integer to a Go-compatible ctypes integer.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
i: The Python integer.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
A ctypes.c_int equivalent.
|
|
76
|
+
"""
|
|
77
|
+
return ctypes.c_int(i)
|
|
78
|
+
|
|
79
|
+
def from_go_int(self, val: ctypes.c_int) -> int:
|
|
80
|
+
"""
|
|
81
|
+
Converts a ctypes.c_int from Go to a Python integer.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
val: The ctypes.c_int value.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
The corresponding Python integer.
|
|
88
|
+
"""
|
|
89
|
+
return int(val)
|
|
90
|
+
|
|
91
|
+
def to_go_float(self, f: float) -> ctypes.c_float:
|
|
92
|
+
"""
|
|
93
|
+
Converts a Python float to a Go-compatible ctypes float.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
f: The Python float.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
A ctypes.c_float equivalent.
|
|
100
|
+
"""
|
|
101
|
+
return ctypes.c_float(f)
|
|
102
|
+
|
|
103
|
+
def from_go_float(self, val: ctypes.c_float) -> float:
|
|
104
|
+
"""
|
|
105
|
+
Converts a ctypes.c_float from Go to a Python float.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
val: The ctypes.c_float value.
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
The corresponding Python float.
|
|
112
|
+
"""
|
|
113
|
+
return float(val)
|
|
114
|
+
|
|
115
|
+
def to_go_bool(self, b: bool) -> ctypes.c_int:
|
|
116
|
+
"""
|
|
117
|
+
Converts a Python boolean to a Go-compatible integer (1 if True, 0 if False).
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
b: The Python boolean.
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
A ctypes.c_int (1 or 0).
|
|
124
|
+
"""
|
|
125
|
+
return ctypes.c_int(1 if b else 0)
|
|
126
|
+
|
|
127
|
+
def from_go_bool(self, val: ctypes.c_int) -> bool:
|
|
128
|
+
"""
|
|
129
|
+
Converts a Go-compatible integer (ctypes.c_int) to a Python boolean.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
val: The ctypes.c_int value.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
True if the value equals 1, False otherwise.
|
|
136
|
+
"""
|
|
137
|
+
return val == 1
|
|
138
|
+
|
|
139
|
+
def to_go_bytes(self, b: bytes) -> ctypes.c_char_p:
|
|
140
|
+
"""
|
|
141
|
+
Converts a Python bytes object to a Go-compatible C char pointer.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
b: The Python bytes.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
A ctypes.c_char_p pointing to the byte data.
|
|
148
|
+
"""
|
|
149
|
+
if b is None:
|
|
150
|
+
b = b""
|
|
151
|
+
buf = ctypes.create_string_buffer(b, len(b))
|
|
152
|
+
return ctypes.cast(buf, ctypes.c_char_p)
|
|
153
|
+
|
|
154
|
+
def from_go_bytes(self, cptr: ctypes.c_char_p, length: int) -> bytes:
|
|
155
|
+
"""
|
|
156
|
+
Converts a Go pointer representing a byte array to a Python bytes object.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
cptr: The C pointer to the byte array.
|
|
160
|
+
length: The number of bytes to read.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
A Python bytes object containing the read data.
|
|
164
|
+
"""
|
|
165
|
+
if not cptr or length <= 0:
|
|
166
|
+
return bytes()
|
|
167
|
+
return ctypes.string_at(cptr, length)
|
|
168
|
+
|
|
169
|
+
def from_go_ptr_to_json(self, c_void_ptr_val: int) -> Any:
|
|
170
|
+
"""
|
|
171
|
+
Converts a C void* pointer (returned by Go as int) pointing to a
|
|
172
|
+
null-terminated C string containing JSON into a Python object.
|
|
173
|
+
|
|
174
|
+
It reads the string, parses it as JSON, and crucially frees the C memory
|
|
175
|
+
using the provided FreeString function from the Go library.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
c_void_ptr_val: The integer value representing the C pointer address.
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
The parsed Python object from the JSON string.
|
|
182
|
+
|
|
183
|
+
Raises:
|
|
184
|
+
GoLibError: If the pointer is NULL, reading/decoding fails, or
|
|
185
|
+
JSON parsing fails.
|
|
186
|
+
TypeError: When go_lib is not a valid ctypes library object.
|
|
187
|
+
"""
|
|
188
|
+
|
|
189
|
+
json_string: Optional[str] = None # To store the string for error reporting
|
|
190
|
+
|
|
191
|
+
if not c_void_ptr_val: # Check if the address is NULL (0)
|
|
192
|
+
raise print("Received a NULL pointer from Go function")
|
|
193
|
+
|
|
194
|
+
try:
|
|
195
|
+
|
|
196
|
+
# --- Double-Free Check (Before Reading/Casting) ---
|
|
197
|
+
self.__freed_pointers_lock.acquire() # Acquire lock if using threading
|
|
198
|
+
if c_void_ptr_val in self.__freed_pointers:
|
|
199
|
+
|
|
200
|
+
# This indicates a serious logic error elsewhere - the pointer
|
|
201
|
+
# was already freed but somehow passed here again.
|
|
202
|
+
logger.warning(f"🔥🔥🔥 ATTEMPT TO PROCESS ALREADY FREED POINTER {hex(c_void_ptr_val)}! 🔥🔥🔥")
|
|
203
|
+
|
|
204
|
+
# Raising an error is safer than trying to read potentially invalid memory.
|
|
205
|
+
logger.error(f"Attempt to process pointer {hex(c_void_ptr_val)} which was already freed",
|
|
206
|
+
pointer_val=c_void_ptr_val)
|
|
207
|
+
raise Exception(f"Attempt to process pointer {hex(c_void_ptr_val)} which was already freed",
|
|
208
|
+
pointer_val=c_void_ptr_val)
|
|
209
|
+
self.__freed_pointers_lock.release() # Release lock if using threading
|
|
210
|
+
|
|
211
|
+
# --- Cast void* to c_char_p and Read String ---
|
|
212
|
+
try:
|
|
213
|
+
|
|
214
|
+
# Perform the cast only when needed for reading
|
|
215
|
+
c_char_ptr_for_read = ctypes.cast(c_void_ptr_val, ctypes.c_char_p)
|
|
216
|
+
raw_bytes = ctypes.string_at(c_char_ptr_for_read)
|
|
217
|
+
json_string = raw_bytes.decode('utf-8')
|
|
218
|
+
|
|
219
|
+
# Logger.debug(f"Read string (len={len(json_string)}) from pointer {hex(c_void_ptr_val)}: %.100s...", json_string)
|
|
220
|
+
except (ctypes.ArgumentError, ValueError, UnicodeDecodeError) as read_err:
|
|
221
|
+
logger.error(f"Failed to read/decode string from pointer {hex(c_void_ptr_val)}: {read_err}", exc_info=False)
|
|
222
|
+
|
|
223
|
+
# Even if reading fails, the pointer itself *might* still be valid C memory
|
|
224
|
+
# that Go expects us to free. We will proceed to free it in finally.
|
|
225
|
+
raise Exception(f"Failed to read string from pointer {hex(c_void_ptr_val)}: {read_err}",
|
|
226
|
+
pointer_val=c_void_ptr_val) from read_err
|
|
227
|
+
except Exception as unexpected_read_err: # Catch other potential ctypes issues
|
|
228
|
+
logger.error(f"Unexpected error reading C string from pointer {hex(c_void_ptr_val)}: {unexpected_read_err}", exc_info=True)
|
|
229
|
+
raise Exception(f"Unexpected error reading C string from pointer {hex(c_void_ptr_val)}: {unexpected_read_err}",
|
|
230
|
+
pointer_val=c_void_ptr_val) from unexpected_read_err
|
|
231
|
+
|
|
232
|
+
# --- Check for Empty String ---
|
|
233
|
+
|
|
234
|
+
# --- Parse JSON ---
|
|
235
|
+
try:
|
|
236
|
+
|
|
237
|
+
# Now that we have the string, parse it
|
|
238
|
+
logger.debug(f"Parsing JSON from string: {json_string}")
|
|
239
|
+
parsed_data = json.loads(json_string)
|
|
240
|
+
logger.debug(f"Parsed JSON data: {parsed_data}")
|
|
241
|
+
|
|
242
|
+
# Logger.debug(f"Successfully parsed JSON from pointer {hex(c_void_ptr_val)}")
|
|
243
|
+
return parsed_data # Return the parsed Python object
|
|
244
|
+
|
|
245
|
+
except json.JSONDecodeError as json_err:
|
|
246
|
+
logger.error(f"Failed to decode JSON from pointer {hex(c_void_ptr_val)}: {json_err}", exc_info=False)
|
|
247
|
+
|
|
248
|
+
# Again, the pointer is likely valid C memory, but the content is bad.
|
|
249
|
+
# Let the block handle freeing.
|
|
250
|
+
raise Exception(f"Failed to decode JSON from pointer {hex(c_void_ptr_val)}: {json_err}",
|
|
251
|
+
pointer_val=c_void_ptr_val) from json_err
|
|
252
|
+
|
|
253
|
+
finally:
|
|
254
|
+
|
|
255
|
+
# --- CRITICAL: Free C Memory ---
|
|
256
|
+
# This block executes even if errors occurred during read/parse,
|
|
257
|
+
# ensuring we attempt to free any non-NULL pointer received from Go.
|
|
258
|
+
with self.__freed_pointers_lock:
|
|
259
|
+
if c_void_ptr_val:
|
|
260
|
+
logger.info(f"🐍 FINALLY: Freeing pointer {hex(c_void_ptr_val)}...")
|
|
261
|
+
if c_void_ptr_val in self.__freed_pointers:
|
|
262
|
+
|
|
263
|
+
# This check is technically redundant if the initial check worked,
|
|
264
|
+
# but provides an extra safety layer in case of concurrency issues
|
|
265
|
+
# (if freed_pointers is shared without locks - which it shouldn't be).
|
|
266
|
+
logger.warning(f"🔥🔥🔥 DOUBLE FREE DETECTED in finally block for {hex(c_void_ptr_val)}! Skipping FreeString call again. 🔥🔥🔥")
|
|
267
|
+
else:
|
|
268
|
+
|
|
269
|
+
# Add before calling free
|
|
270
|
+
try:
|
|
271
|
+
self.libp2p.FreeString(c_void_ptr_val) # Pass the original void* value
|
|
272
|
+
logger.info(f"✅ FINALLY: FreeString successful for {hex(c_void_ptr_val)}.")
|
|
273
|
+
except Exception as free_err:
|
|
274
|
+
|
|
275
|
+
# Log if FreeString fails, but don't raise from finally
|
|
276
|
+
# as it might hide the original error.
|
|
277
|
+
logger.critical(f"🚨 FAILED TO FREE C MEMORY for pointer {hex(c_void_ptr_val)} via FreeString: {free_err}", exc_info=True)
|
|
278
|
+
|
|
279
|
+
# Consider removing from freed_pointers if free failed?
|
|
280
|
+
# freed_pointers.discard(c_void_ptr_val) # Maybe, to allow retry? Risky.
|
|
281
|
+
# But if FreeString fails, the pointer is likely invalid anyway.
|
|
282
|
+
|
|
283
|
+
def to_go_json(self, data: Any) -> bytes:
|
|
284
|
+
"""
|
|
285
|
+
Encodes a Python object to a JSON string, returning a UTF-8 encoded
|
|
286
|
+
Python 'bytes' object.
|
|
287
|
+
|
|
288
|
+
This 'bytes' object is suitable for direct use with ctypes when passing
|
|
289
|
+
to a C function expecting a 'char*' (ctypes.c_char_p).
|
|
290
|
+
|
|
291
|
+
Args:
|
|
292
|
+
data: The Python object (e.g., dict, list) to encode.
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
A Python 'bytes' object containing the JSON string, UTF-8 encoded.
|
|
296
|
+
"""
|
|
297
|
+
json_str = json.dumps(data)
|
|
298
|
+
return self.to_go_string(json_str)
|
|
299
|
+
|
|
300
|
+
def from_go_string_to_list(self, cstr: ctypes.c_char_p) -> List[Any]:
|
|
301
|
+
"""
|
|
302
|
+
Decodes a JSON-encoded list from a Go C char pointer into a Python list.
|
|
303
|
+
|
|
304
|
+
Args:
|
|
305
|
+
cstr: The Go string (C char pointer) containing a JSON list.
|
|
306
|
+
|
|
307
|
+
Returns:
|
|
308
|
+
A Python list representing the JSON data.
|
|
309
|
+
"""
|
|
310
|
+
s = self.from_go_string(cstr)
|
|
311
|
+
|
|
312
|
+
return json.loads(s)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""
|
|
2
|
+
█████ █████ ██████ █████ █████ █████ █████ ██████████ ███████████ █████████ ██████████
|
|
3
|
+
░░███ ░░███ ░░██████ ░░███ ░░███ ░░███ ░░███ ░░███░░░░░█░░███░░░░░███ ███░░░░░███░░███░░░░░█
|
|
4
|
+
░███ ░███ ░███░███ ░███ ██████ ░███ ░███ ░███ ░███ █ ░ ░███ ░███ ░███ ░░░ ░███ █ ░
|
|
5
|
+
░███ ░███ ░███░░███░███ ░░░░░███ ░███ ░███ ░███ ░██████ ░██████████ ░░█████████ ░██████
|
|
6
|
+
░███ ░███ ░███ ░░██████ ███████ ░███ ░░███ ███ ░███░░█ ░███░░░░░███ ░░░░░░░░███ ░███░░█
|
|
7
|
+
░███ ░███ ░███ ░░█████ ███░░███ ░███ ░░░█████░ ░███ ░ █ ░███ ░███ ███ ░███ ░███ ░ █
|
|
8
|
+
░░████████ █████ ░░█████░░████████ █████ ░░███ ██████████ █████ █████░░█████████ ██████████
|
|
9
|
+
░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░ ░░░░░ ░░░ ░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░ ░░░░░░░░░░
|
|
10
|
+
A Collectionless AI Project (https://collectionless.ai)
|
|
11
|
+
Registration/Login: https://unaiverse.io
|
|
12
|
+
Code Repositories: https://github.com/collectionlessai/
|
|
13
|
+
Main Developers: Stefano Melacci (Project Leader), Christian Di Maio, Tommaso Guidi
|
|
14
|
+
"""
|
|
15
|
+
from google.protobuf import descriptor as _descriptor
|
|
16
|
+
from google.protobuf.internal import builder as _builder
|
|
17
|
+
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
18
|
+
from google.protobuf import runtime_version as _runtime_version
|
|
19
|
+
from google.protobuf import symbol_database as _symbol_database
|
|
20
|
+
_runtime_version.ValidateProtobufRuntimeVersion(
|
|
21
|
+
_runtime_version.Domain.PUBLIC,
|
|
22
|
+
6,
|
|
23
|
+
31,
|
|
24
|
+
1,
|
|
25
|
+
'',
|
|
26
|
+
'message.proto'
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# @@protoc_insertion_point(imports)
|
|
30
|
+
|
|
31
|
+
_sym_db = _symbol_database.Default()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rmessage.proto\x12\x03p2p\"\xc0\x01\n\x07Message\x12\x0e\n\x06sender\x18\x01 \x01(\t\x12\x14\n\x0c\x63ontent_type\x18\x02 \x01(\t\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\t\x12\x11\n\tpiggyback\x18\x04 \x01(\t\x12\x15\n\rtimestamp_net\x18\x05 \x01(\t\x12\x31\n\rstream_sample\x18\x06 \x01(\x0b\x32\x18.p2p.StreamSampleContentH\x00\x12\x16\n\x0cjson_content\x18\x07 \x01(\tH\x00\x42\t\n\x07\x63ontent\"\x90\x01\n\x13StreamSampleContent\x12\x36\n\x07samples\x18\x01 \x03(\x0b\x32%.p2p.StreamSampleContent.SamplesEntry\x1a\x41\n\x0cSamplesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12 \n\x05value\x18\x02 \x01(\x0b\x32\x11.p2p.StreamSample:\x02\x38\x01\"e\n\x0cStreamSample\x12\x1d\n\x04\x64\x61ta\x18\x01 \x01(\x0b\x32\x0f.p2p.SampleData\x12\x10\n\x08\x64\x61ta_tag\x18\x02 \x01(\x05\x12\x16\n\tdata_uuid\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0c\n\n_data_uuid\"\x8e\x01\n\nSampleData\x12&\n\x0btensor_data\x18\x01 \x01(\x0b\x32\x0f.p2p.TensorDataH\x00\x12$\n\nimage_data\x18\x02 \x01(\x0b\x32\x0e.p2p.ImageDataH\x00\x12\"\n\ttext_data\x18\x03 \x01(\x0b\x32\r.p2p.TextDataH\x00\x42\x0e\n\x0c\x64\x61ta_payload\"8\n\nTensorData\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\x12\r\n\x05\x64type\x18\x02 \x01(\t\x12\r\n\x05shape\x18\x03 \x03(\x05\"\x19\n\tImageData\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"\x18\n\x08TextData\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\tB\x0cZ\n./proto-gob\x06proto3')
|
|
37
|
+
|
|
38
|
+
_globals = globals()
|
|
39
|
+
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
40
|
+
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'message_pb2', _globals)
|
|
41
|
+
if not _descriptor._USE_C_DESCRIPTORS:
|
|
42
|
+
_globals['DESCRIPTOR']._loaded_options = None
|
|
43
|
+
_globals['DESCRIPTOR']._serialized_options = b'Z\n./proto-go'
|
|
44
|
+
_globals['_STREAMSAMPLECONTENT_SAMPLESENTRY']._loaded_options = None
|
|
45
|
+
_globals['_STREAMSAMPLECONTENT_SAMPLESENTRY']._serialized_options = b'8\001'
|
|
46
|
+
_globals['_MESSAGE']._serialized_start=23
|
|
47
|
+
_globals['_MESSAGE']._serialized_end=215
|
|
48
|
+
_globals['_STREAMSAMPLECONTENT']._serialized_start=218
|
|
49
|
+
_globals['_STREAMSAMPLECONTENT']._serialized_end=362
|
|
50
|
+
_globals['_STREAMSAMPLECONTENT_SAMPLESENTRY']._serialized_start=297
|
|
51
|
+
_globals['_STREAMSAMPLECONTENT_SAMPLESENTRY']._serialized_end=362
|
|
52
|
+
_globals['_STREAMSAMPLE']._serialized_start=364
|
|
53
|
+
_globals['_STREAMSAMPLE']._serialized_end=465
|
|
54
|
+
_globals['_SAMPLEDATA']._serialized_start=468
|
|
55
|
+
_globals['_SAMPLEDATA']._serialized_end=610
|
|
56
|
+
_globals['_TENSORDATA']._serialized_start=612
|
|
57
|
+
_globals['_TENSORDATA']._serialized_end=668
|
|
58
|
+
_globals['_IMAGEDATA']._serialized_start=670
|
|
59
|
+
_globals['_IMAGEDATA']._serialized_end=695
|
|
60
|
+
_globals['_TEXTDATA']._serialized_start=697
|
|
61
|
+
_globals['_TEXTDATA']._serialized_end=721
|
|
62
|
+
|
|
63
|
+
# @@protoc_insertion_point(module_scope)
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"""
|
|
2
|
+
█████ █████ ██████ █████ █████ █████ █████ ██████████ ███████████ █████████ ██████████
|
|
3
|
+
░░███ ░░███ ░░██████ ░░███ ░░███ ░░███ ░░███ ░░███░░░░░█░░███░░░░░███ ███░░░░░███░░███░░░░░█
|
|
4
|
+
░███ ░███ ░███░███ ░███ ██████ ░███ ░███ ░███ ░███ █ ░ ░███ ░███ ░███ ░░░ ░███ █ ░
|
|
5
|
+
░███ ░███ ░███░░███░███ ░░░░░███ ░███ ░███ ░███ ░██████ ░██████████ ░░█████████ ░██████
|
|
6
|
+
░███ ░███ ░███ ░░██████ ███████ ░███ ░░███ ███ ░███░░█ ░███░░░░░███ ░░░░░░░░███ ░███░░█
|
|
7
|
+
░███ ░███ ░███ ░░█████ ███░░███ ░███ ░░░█████░ ░███ ░ █ ░███ ░███ ███ ░███ ░███ ░ █
|
|
8
|
+
░░████████ █████ ░░█████░░████████ █████ ░░███ ██████████ █████ █████░░█████████ ██████████
|
|
9
|
+
░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░ ░░░░░ ░░░ ░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░ ░░░░░░░░░░
|
|
10
|
+
A Collectionless AI Project (https://collectionless.ai)
|
|
11
|
+
Registration/Login: https://unaiverse.io
|
|
12
|
+
Code Repositories: https://github.com/collectionlessai/
|
|
13
|
+
Main Developers: Stefano Melacci (Project Leader), Christian Di Maio, Tommaso Guidi
|
|
14
|
+
"""
|
|
15
|
+
import io
|
|
16
|
+
import gzip
|
|
17
|
+
import json
|
|
18
|
+
import torch
|
|
19
|
+
from PIL import Image
|
|
20
|
+
from datetime import datetime, timezone
|
|
21
|
+
|
|
22
|
+
# Import the Protobuf-generated module
|
|
23
|
+
try:
|
|
24
|
+
from . import message_pb2 as pb
|
|
25
|
+
except ImportError:
|
|
26
|
+
print("Error: message_pb2.py not found. Please compile the .proto file first.")
|
|
27
|
+
raise
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Msg:
|
|
31
|
+
|
|
32
|
+
# Message content types
|
|
33
|
+
PROFILE = "profile"
|
|
34
|
+
WORLD_APPROVAL = "world_approval"
|
|
35
|
+
AGENT_APPROVAL = "agent_approval"
|
|
36
|
+
PROFILE_REQUEST = "profile_request"
|
|
37
|
+
ADDRESS_UPDATE = "address_update"
|
|
38
|
+
STREAM_SAMPLE = "stream_sample"
|
|
39
|
+
ACTION_REQUEST = "action_request"
|
|
40
|
+
ROLE_SUGGESTION = "role_suggestion"
|
|
41
|
+
HSM = "hsm"
|
|
42
|
+
MISC = "misc"
|
|
43
|
+
GET_CV_FROM_ROOT = "get_cv_from_root"
|
|
44
|
+
BADGE_SUGGESTIONS = "badge_suggestions"
|
|
45
|
+
INSPECT_ON = "inspect_on"
|
|
46
|
+
INSPECT_CMD = "inspect_cmd"
|
|
47
|
+
WORLD_AGENTS_LIST = "world_agents_list"
|
|
48
|
+
CONSOLE_AND_BEHAV_STATUS = "console_and_behav_status"
|
|
49
|
+
|
|
50
|
+
# Collections
|
|
51
|
+
CONTENT_TYPES = {PROFILE, WORLD_APPROVAL, AGENT_APPROVAL, PROFILE_REQUEST, ADDRESS_UPDATE,
|
|
52
|
+
STREAM_SAMPLE, ACTION_REQUEST, ROLE_SUGGESTION, HSM, MISC, GET_CV_FROM_ROOT,
|
|
53
|
+
BADGE_SUGGESTIONS, INSPECT_ON, INSPECT_CMD, WORLD_AGENTS_LIST, CONSOLE_AND_BEHAV_STATUS}
|
|
54
|
+
|
|
55
|
+
def __init__(self,
|
|
56
|
+
sender: str | None = None,
|
|
57
|
+
content: any = None,
|
|
58
|
+
timestamp_net: str | None = None,
|
|
59
|
+
channel: str | None = None,
|
|
60
|
+
content_type: str = MISC,
|
|
61
|
+
piggyback: str | None = None,
|
|
62
|
+
_proto_msg: pb.Message = None):
|
|
63
|
+
"""The constructor should be used either to create a new message filling the fields,
|
|
64
|
+
or to parse an existing Protobuf message (passing _proto_msg). In the latter case,
|
|
65
|
+
the other fields are ignored and the Protobuf message is used as-is. The message is
|
|
66
|
+
simply stored in the internal `_proto_msg` field and other fields can be accessed
|
|
67
|
+
through properties."""
|
|
68
|
+
|
|
69
|
+
self._decoded_content: any = None # Cache for decompressed content
|
|
70
|
+
|
|
71
|
+
if _proto_msg is not None:
|
|
72
|
+
|
|
73
|
+
# Check if any other arguments were simultaneously provided
|
|
74
|
+
other_args = [sender, content, timestamp_net, channel, piggyback]
|
|
75
|
+
if any(arg is not None for arg in other_args):
|
|
76
|
+
raise ValueError("Cannot specify other arguments when creating a Msg from a _proto_msg.")
|
|
77
|
+
|
|
78
|
+
# This path is used by from_bytes, message is already built
|
|
79
|
+
self._proto_msg = _proto_msg
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
# Sanity checks
|
|
83
|
+
assert sender is not None, "Sender must be specified for a new message."
|
|
84
|
+
assert isinstance(sender, str), "Sender must be a string"
|
|
85
|
+
assert timestamp_net is None or isinstance(timestamp_net, str), "Invalid timestamp_net"
|
|
86
|
+
assert channel is None or isinstance(channel, str), "Invalid channel"
|
|
87
|
+
assert content_type in Msg.CONTENT_TYPES, "Invalid content type"
|
|
88
|
+
|
|
89
|
+
# --- SMART CONSTRUCTOR: Populates the correct 'oneof' field ---
|
|
90
|
+
self._proto_msg = pb.Message()
|
|
91
|
+
self._proto_msg.sender = sender if sender is not None else ""
|
|
92
|
+
self._proto_msg.timestamp_net = timestamp_net if timestamp_net is not None else \
|
|
93
|
+
datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S.%f")
|
|
94
|
+
self._proto_msg.content_type = content_type if content_type is not None else self.MISC
|
|
95
|
+
self._proto_msg.channel = channel if channel is not None else "<unknown>"
|
|
96
|
+
self._proto_msg.piggyback = piggyback if piggyback is not None else ""
|
|
97
|
+
|
|
98
|
+
if content is None or content == "<empty>":
|
|
99
|
+
return # Nothing to set in the 'oneof'
|
|
100
|
+
|
|
101
|
+
# Route the content to the correct builder
|
|
102
|
+
if content_type == Msg.STREAM_SAMPLE:
|
|
103
|
+
self._build_stream_sample_content(content)
|
|
104
|
+
else:
|
|
105
|
+
|
|
106
|
+
# All other structured types use the generic json_content field
|
|
107
|
+
self._build_json_content(content)
|
|
108
|
+
|
|
109
|
+
def __str__(self):
|
|
110
|
+
return (f"Msg(sender={self.sender[:12]}..., content_type={self.content_type}, "
|
|
111
|
+
f"channel='{self.channel}', content_len={len(self.to_bytes())} bytes)")
|
|
112
|
+
|
|
113
|
+
# --- Properties for lazy-loading and easy access ---
|
|
114
|
+
@property
|
|
115
|
+
def sender(self):
|
|
116
|
+
return self._proto_msg.sender
|
|
117
|
+
|
|
118
|
+
@sender.setter
|
|
119
|
+
def sender(self, value: str):
|
|
120
|
+
self._proto_msg.sender = value if value is not None else ""
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def content_type(self):
|
|
124
|
+
return self._proto_msg.content_type
|
|
125
|
+
|
|
126
|
+
@content_type.setter
|
|
127
|
+
def content_type(self, value: str):
|
|
128
|
+
self._proto_msg.content_type = value if value is not None else self.MISC
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def channel(self):
|
|
132
|
+
return self._proto_msg.channel
|
|
133
|
+
|
|
134
|
+
@channel.setter
|
|
135
|
+
def channel(self, value: str):
|
|
136
|
+
self._proto_msg.channel = value if value is not None else "<unknown>"
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def piggyback(self):
|
|
140
|
+
return self._proto_msg.piggyback
|
|
141
|
+
|
|
142
|
+
@piggyback.setter
|
|
143
|
+
def piggyback(self, value: str):
|
|
144
|
+
self._proto_msg.piggyback = value if value is not None else ""
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def timestamp_net(self):
|
|
148
|
+
return self._proto_msg.timestamp_net
|
|
149
|
+
|
|
150
|
+
@timestamp_net.setter
|
|
151
|
+
def timestamp_net(self, value: str):
|
|
152
|
+
self._proto_msg.timestamp_net = value if value is not None else ""
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def content(self) -> any:
|
|
157
|
+
"""The main content of the message, decoded on-the-fly with caching."""
|
|
158
|
+
if self._decoded_content is not None:
|
|
159
|
+
return self._decoded_content
|
|
160
|
+
|
|
161
|
+
payload_type = self._proto_msg.WhichOneof("content")
|
|
162
|
+
if payload_type == "stream_sample":
|
|
163
|
+
self._decoded_content = self._parse_stream_sample_content()
|
|
164
|
+
elif payload_type == "json_content":
|
|
165
|
+
self._decoded_content = self._parse_json_content()
|
|
166
|
+
else:
|
|
167
|
+
self._decoded_content = "<empty>"
|
|
168
|
+
|
|
169
|
+
return self._decoded_content
|
|
170
|
+
|
|
171
|
+
# --- Serialization / Deserialization ---
|
|
172
|
+
def to_bytes(self) -> bytes:
|
|
173
|
+
"""Serializes the internal Protobuf message to bytes."""
|
|
174
|
+
return self._proto_msg.SerializeToString()
|
|
175
|
+
|
|
176
|
+
@classmethod
|
|
177
|
+
def from_bytes(cls, msg_bytes: bytes) -> 'Msg':
|
|
178
|
+
"""Deserializes a byte array into a new Msg instance."""
|
|
179
|
+
pb_msg = pb.Message()
|
|
180
|
+
pb_msg.ParseFromString(msg_bytes)
|
|
181
|
+
|
|
182
|
+
# Pass the parsed protobuf message to the constructor
|
|
183
|
+
return cls(_proto_msg=pb_msg)
|
|
184
|
+
|
|
185
|
+
# --- Internal Helper Methods ---
|
|
186
|
+
def _build_json_content(self, content: dict):
|
|
187
|
+
"""Populates the generic json_content field."""
|
|
188
|
+
self._proto_msg.json_content = json.dumps(content)
|
|
189
|
+
|
|
190
|
+
def _parse_json_content(self) -> dict:
|
|
191
|
+
"""Parses the generic json_content field back to a dict."""
|
|
192
|
+
return json.loads(self._proto_msg.json_content)
|
|
193
|
+
|
|
194
|
+
def _build_stream_sample_content(self, samples_dict: dict):
|
|
195
|
+
"""Builds the complex StreamSampleContent message from a dict."""
|
|
196
|
+
content_pb = self._proto_msg.stream_sample
|
|
197
|
+
for name, sample_info in samples_dict.items():
|
|
198
|
+
data = sample_info.get('data')
|
|
199
|
+
if data is None:
|
|
200
|
+
continue
|
|
201
|
+
|
|
202
|
+
stream_sample_pb = content_pb.samples[name]
|
|
203
|
+
stream_sample_pb.data_tag = sample_info.get('data_tag', -1)
|
|
204
|
+
uuid = sample_info.get('data_uuid')
|
|
205
|
+
|
|
206
|
+
# Only set the field if the uuid is not None
|
|
207
|
+
if uuid is not None:
|
|
208
|
+
stream_sample_pb.data_uuid = uuid
|
|
209
|
+
|
|
210
|
+
if isinstance(data, torch.Tensor):
|
|
211
|
+
raw_bytes = data.detach().cpu().numpy().tobytes()
|
|
212
|
+
with io.BytesIO() as buffer:
|
|
213
|
+
with gzip.GzipFile(fileobj=buffer, mode='wb') as f:
|
|
214
|
+
f.write(raw_bytes)
|
|
215
|
+
stream_sample_pb.data.tensor_data.data = buffer.getvalue()
|
|
216
|
+
stream_sample_pb.data.tensor_data.dtype = str(data.dtype).split('.')[-1]
|
|
217
|
+
stream_sample_pb.data.tensor_data.shape.extend(list(data.shape))
|
|
218
|
+
|
|
219
|
+
elif isinstance(data, Image.Image):
|
|
220
|
+
with io.BytesIO() as buffer:
|
|
221
|
+
data.save(buffer, format="PNG", optimize=True, compress_level=9)
|
|
222
|
+
stream_sample_pb.data.image_data.data = buffer.getvalue()
|
|
223
|
+
|
|
224
|
+
elif isinstance(data, str):
|
|
225
|
+
stream_sample_pb.data.text_data.data = data
|
|
226
|
+
|
|
227
|
+
def _parse_stream_sample_content(self) -> dict:
|
|
228
|
+
"""
|
|
229
|
+
Parses the internal StreamSampleContent message back into a Python
|
|
230
|
+
dictionary of tensors and images.
|
|
231
|
+
"""
|
|
232
|
+
py_dict = {}
|
|
233
|
+
|
|
234
|
+
# Iterate through the Protobuf map ('samples')
|
|
235
|
+
for name, sample_pb in self._proto_msg.stream_sample.samples.items():
|
|
236
|
+
data_payload = sample_pb.data
|
|
237
|
+
data = None
|
|
238
|
+
|
|
239
|
+
# Check which field in the 'oneof' is set
|
|
240
|
+
payload_type = data_payload.WhichOneof("data_payload")
|
|
241
|
+
|
|
242
|
+
if payload_type == "tensor_data":
|
|
243
|
+
tensor_data = data_payload.tensor_data
|
|
244
|
+
|
|
245
|
+
# Decompress and reconstruct the tensor
|
|
246
|
+
with gzip.GzipFile(fileobj=io.BytesIO(tensor_data.data), mode='rb') as f:
|
|
247
|
+
raw_bytes = f.read()
|
|
248
|
+
data = torch.frombuffer(
|
|
249
|
+
bytearray(raw_bytes),
|
|
250
|
+
dtype=getattr(torch, tensor_data.dtype)
|
|
251
|
+
).reshape(list(tensor_data.shape))
|
|
252
|
+
|
|
253
|
+
elif payload_type == "image_data":
|
|
254
|
+
data = Image.open(io.BytesIO(data_payload.image_data.data))
|
|
255
|
+
|
|
256
|
+
elif payload_type == "text_data":
|
|
257
|
+
data = data_payload.text_data.data
|
|
258
|
+
|
|
259
|
+
# Build the final Python dictionary for this sample
|
|
260
|
+
py_dict[name] = {
|
|
261
|
+
'data': data,
|
|
262
|
+
'data_tag': sample_pb.data_tag,
|
|
263
|
+
'data_uuid': sample_pb.data_uuid if sample_pb.HasField("data_uuid") else None
|
|
264
|
+
}
|
|
265
|
+
return py_dict
|