unaiverse 0.1.8__cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_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 +2041 -0
- unaiverse/clock.py +191 -0
- unaiverse/dataprops.py +1209 -0
- unaiverse/hsm.py +1889 -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 +710 -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 +2299 -0
- unaiverse/networking/node/profile.py +447 -0
- unaiverse/networking/node/tokens.py +79 -0
- unaiverse/networking/p2p/__init__.py +188 -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 +2527 -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 +268 -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-312-aarch64-linux-gnu.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 +492 -0
- unaiverse/utils/sandbox.py +293 -0
- unaiverse/utils/server.py +435 -0
- unaiverse/world.py +353 -0
- unaiverse-0.1.8.dist-info/METADATA +365 -0
- unaiverse-0.1.8.dist-info/RECORD +50 -0
- unaiverse-0.1.8.dist-info/WHEEL +7 -0
- unaiverse-0.1.8.dist-info/licenses/LICENSE +43 -0
- unaiverse-0.1.8.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,293 @@
|
|
|
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 os
|
|
16
|
+
import sys
|
|
17
|
+
import uuid
|
|
18
|
+
import argparse
|
|
19
|
+
import subprocess
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from unaiverse.networking.p2p import P2P
|
|
22
|
+
|
|
23
|
+
# Configuration
|
|
24
|
+
DOCKER_IMAGE_NAME = "unaiverse-sandbox"
|
|
25
|
+
CONTAINER_NAME_BASE = "unaiverse-sandbox-container"
|
|
26
|
+
CONTAINER_NAME = f"{CONTAINER_NAME_BASE}-{uuid.uuid4().hex[:8]}" # Append a short unique ID
|
|
27
|
+
DOCKERFILE_CONTENT = """
|
|
28
|
+
|
|
29
|
+
# Debian image, automatically guessed architecture
|
|
30
|
+
FROM python:3.12-slim-bookworm
|
|
31
|
+
|
|
32
|
+
# Installing Go compiler
|
|
33
|
+
RUN apt-get update && apt-get install -y --no-install-recommends build-essential curl git
|
|
34
|
+
RUN rm -rf /var/lib/apt/lists/*
|
|
35
|
+
RUN ARCH=$(dpkg --print-architecture) && curl -LO https://go.dev/dl/go1.24.5.linux-${ARCH}.tar.gz
|
|
36
|
+
RUN ARCH=$(dpkg --print-architecture) && tar -C /usr/local -xzf go1.24.5.linux-${ARCH}.tar.gz
|
|
37
|
+
RUN ARCH=$(dpkg --print-architecture) && rm go1.24.5.linux-${ARCH}.tar.gz
|
|
38
|
+
|
|
39
|
+
# Set Go environment variables
|
|
40
|
+
ENV PATH="/usr/local/go/bin:${PATH}"
|
|
41
|
+
ENV GOPATH="/go"
|
|
42
|
+
RUN mkdir -p /go/bin /go/src /go/pkg
|
|
43
|
+
|
|
44
|
+
# Setting the working directory inside the container
|
|
45
|
+
WORKDIR /unaiverse
|
|
46
|
+
|
|
47
|
+
# Dependencies
|
|
48
|
+
RUN <create_requirements.txt>
|
|
49
|
+
RUN pip install --no-cache-dir -r requirements.txt --break-system-packages
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def sandbox(file_to_run: str,
|
|
54
|
+
read_only_paths: tuple[str] | list[str] | None = None,
|
|
55
|
+
writable_paths: tuple[str] | list[str] | None = None) -> None:
|
|
56
|
+
|
|
57
|
+
# Path of this file
|
|
58
|
+
absolute_path_of_this_file = os.path.abspath(__file__)
|
|
59
|
+
|
|
60
|
+
# Folders composing the path (and file name at the end)
|
|
61
|
+
path_components = list(Path(absolute_path_of_this_file).parts)
|
|
62
|
+
|
|
63
|
+
# Ensuring the folder/file structure was not manipulated
|
|
64
|
+
assert path_components[-1] == 'sandbox.py', "Major security issue, stopping."
|
|
65
|
+
assert path_components[-2] == 'utils', "Major security issue, stopping."
|
|
66
|
+
assert path_components[-3] == 'unaiverse', "Major security issue, stopping."
|
|
67
|
+
|
|
68
|
+
# Main folder of UNaIVERSE
|
|
69
|
+
abspath_of_unaiverse_code = str(Path(*path_components[0:-3]))
|
|
70
|
+
|
|
71
|
+
# Clean up any remnants from previous runs first (safety)
|
|
72
|
+
cleanup_docker_artifacts(where=abspath_of_unaiverse_code)
|
|
73
|
+
|
|
74
|
+
# Requirements
|
|
75
|
+
echoed_contents_of_requirements = 'printf "'
|
|
76
|
+
with open(os.path.join(abspath_of_unaiverse_code, "requirements.txt"), 'r') as req_file:
|
|
77
|
+
req_lines = req_file.readlines()
|
|
78
|
+
for i, req_line in enumerate(req_lines):
|
|
79
|
+
if i != (len(req_lines) - 1) and len(req_line.strip()) > 0:
|
|
80
|
+
echoed_contents_of_requirements += req_line.strip() + "\\n"
|
|
81
|
+
else:
|
|
82
|
+
echoed_contents_of_requirements += req_line.strip() + "\\n\" > requirements.txt"
|
|
83
|
+
|
|
84
|
+
# Create Dockerfile
|
|
85
|
+
print("Creating Dockerfile...")
|
|
86
|
+
with open(os.path.join(abspath_of_unaiverse_code, "Dockerfile"), "w") as f:
|
|
87
|
+
f.write(DOCKERFILE_CONTENT.replace('<create_requirements.txt>', echoed_contents_of_requirements))
|
|
88
|
+
|
|
89
|
+
# Building Docker image
|
|
90
|
+
if not build_docker_image(where=abspath_of_unaiverse_code):
|
|
91
|
+
print("Exiting due to Docker image build failure")
|
|
92
|
+
cleanup_docker_artifacts(where=abspath_of_unaiverse_code) # Try to clean up what was created (if any)
|
|
93
|
+
sys.exit(1)
|
|
94
|
+
|
|
95
|
+
# Read only folders from the host machine
|
|
96
|
+
read_only_mount_paths = ([abspath_of_unaiverse_code] +
|
|
97
|
+
(list(read_only_paths) if read_only_paths is not None else []))
|
|
98
|
+
|
|
99
|
+
# Writable folders in host machine
|
|
100
|
+
writable_mount_paths = ([os.path.join(abspath_of_unaiverse_code, 'runners'),
|
|
101
|
+
os.path.join(abspath_of_unaiverse_code, 'unaiverse', 'library'),
|
|
102
|
+
os.path.join(abspath_of_unaiverse_code, 'unaiverse', 'networking', 'p2p')] +
|
|
103
|
+
(list(writable_paths) if writable_paths is not None else []))
|
|
104
|
+
|
|
105
|
+
# Running
|
|
106
|
+
if not run_in_docker(file_to_run=os.path.abspath(file_to_run),
|
|
107
|
+
read_only_host_paths=read_only_mount_paths,
|
|
108
|
+
writable_host_paths=writable_mount_paths):
|
|
109
|
+
print("Exiting due to Docker container run failure")
|
|
110
|
+
sys.exit(1)
|
|
111
|
+
|
|
112
|
+
# Final cleanup
|
|
113
|
+
cleanup_docker_artifacts(where=abspath_of_unaiverse_code)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def build_docker_image(where: str):
|
|
117
|
+
"""Builds the Docker image."""
|
|
118
|
+
print(f"Building Docker image '{DOCKER_IMAGE_NAME}'...")
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
|
|
122
|
+
# The '.' at the end means build from the current directory
|
|
123
|
+
subprocess.run(["docker", "build", "-t", DOCKER_IMAGE_NAME, where], check=True)
|
|
124
|
+
print(f"Docker image '{DOCKER_IMAGE_NAME}' built successfully.")
|
|
125
|
+
return True
|
|
126
|
+
except subprocess.CalledProcessError as e:
|
|
127
|
+
print(f"Error building Docker image: {e}")
|
|
128
|
+
return False
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def cleanup_docker_artifacts(where: str):
|
|
132
|
+
"""Cleans up the generated files and Docker image."""
|
|
133
|
+
print("Cleaning...")
|
|
134
|
+
|
|
135
|
+
# Stop and remove container if it's still running (e.g., if previous run failed)
|
|
136
|
+
try:
|
|
137
|
+
print(f"Attempting to stop and remove container '{CONTAINER_NAME}' (if running)...")
|
|
138
|
+
subprocess.run(["docker", "stop", CONTAINER_NAME],
|
|
139
|
+
check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
140
|
+
subprocess.run(["docker", "rm", CONTAINER_NAME],
|
|
141
|
+
check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
142
|
+
except Exception as e:
|
|
143
|
+
print(f"Error during preliminary container cleanup: {e}")
|
|
144
|
+
|
|
145
|
+
# Remove the Docker image
|
|
146
|
+
try:
|
|
147
|
+
print(f"Removing Docker image '{DOCKER_IMAGE_NAME}'...")
|
|
148
|
+
subprocess.run(["docker", "rmi", DOCKER_IMAGE_NAME], check=True)
|
|
149
|
+
print("Docker image removed.")
|
|
150
|
+
except subprocess.CalledProcessError as e:
|
|
151
|
+
print(f"Error removing Docker image '{DOCKER_IMAGE_NAME}': {e}")
|
|
152
|
+
|
|
153
|
+
# Remove the generated Dockerfile
|
|
154
|
+
if os.path.exists(os.path.join(where, "Dockerfile")):
|
|
155
|
+
os.remove(os.path.join(where, "Dockerfile"))
|
|
156
|
+
print("Removed Dockerfile.")
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def run_in_docker(file_to_run: str, read_only_host_paths: list[str] = None, writable_host_paths: list[str] = None):
|
|
160
|
+
"""Runs the code in a Docker container with optional mounts."""
|
|
161
|
+
print(f"\nRunning code in Docker container '{CONTAINER_NAME}'...")
|
|
162
|
+
|
|
163
|
+
# Building command (it will continue below...)
|
|
164
|
+
command = ["docker", "run",
|
|
165
|
+
"--rm", # Automatically remove the container when it exits
|
|
166
|
+
"-e", "PYTHONUNBUFFERED=1", # Ensure Python output is unbuffered
|
|
167
|
+
"-e", "NODE_STARTING_PORT",
|
|
168
|
+
"--name", CONTAINER_NAME]
|
|
169
|
+
|
|
170
|
+
if sys.platform.startswith('linux'):
|
|
171
|
+
|
|
172
|
+
# Linux
|
|
173
|
+
command.extend(["--net", "host"]), # Expose the host network (in macOS and Windows it is still a virtual host)
|
|
174
|
+
else:
|
|
175
|
+
|
|
176
|
+
# Not-linux: check ports (adding -p port:port)
|
|
177
|
+
port_int = int(os.getenv("NODE_STARTING_PORT", "0"))
|
|
178
|
+
if port_int > 0:
|
|
179
|
+
command.extend(["-p", str(port_int + 0) + ":" + str(port_int + 0)])
|
|
180
|
+
command.extend(["-p", str(port_int + 1) + ":" + str(port_int + 1) + "/udp"])
|
|
181
|
+
command.extend(["-p", str(port_int + 2) + ":" + str(port_int + 2)])
|
|
182
|
+
command.extend(["-p", str(port_int + 3) + ":" + str(port_int + 3) + "/udp"])
|
|
183
|
+
|
|
184
|
+
# Add read-only mount if path is provided
|
|
185
|
+
if read_only_host_paths is not None and len(read_only_host_paths) > 0:
|
|
186
|
+
for path in read_only_host_paths:
|
|
187
|
+
|
|
188
|
+
# Ensure the host path exists and is a directory
|
|
189
|
+
if not os.path.isdir(path):
|
|
190
|
+
print(
|
|
191
|
+
f"Error: Read-only host path '{path}' does not exist or is not a directory. Cannot mount.")
|
|
192
|
+
return False
|
|
193
|
+
else:
|
|
194
|
+
|
|
195
|
+
# Augmenting command
|
|
196
|
+
path = os.path.abspath(path)
|
|
197
|
+
command.extend(["-v", f"{path}:{path}:ro"])
|
|
198
|
+
print(f"Mounted host '{path}' as read-only to container")
|
|
199
|
+
|
|
200
|
+
# Add writable mount if path is provided
|
|
201
|
+
if writable_host_paths is not None and len(writable_host_paths) > 0:
|
|
202
|
+
for path in writable_host_paths:
|
|
203
|
+
|
|
204
|
+
# Ensure the host path exists and is a directory
|
|
205
|
+
if not os.path.isdir(path):
|
|
206
|
+
print(
|
|
207
|
+
f"Error: Writable host path '{path}' does not exist or is not a directory. Cannot mount.")
|
|
208
|
+
return False
|
|
209
|
+
else:
|
|
210
|
+
|
|
211
|
+
# Augmenting command
|
|
212
|
+
path = os.path.abspath(path)
|
|
213
|
+
command.extend(["-v", f"{path}:{path}"])
|
|
214
|
+
print(f"Mounted host '{path}' as writable to container")
|
|
215
|
+
|
|
216
|
+
# Completing command
|
|
217
|
+
command.append(DOCKER_IMAGE_NAME)
|
|
218
|
+
|
|
219
|
+
try:
|
|
220
|
+
|
|
221
|
+
# Running the prepared command... (using Popen to stream output in real-time)
|
|
222
|
+
try:
|
|
223
|
+
command.extend(["python3", file_to_run])
|
|
224
|
+
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
|
|
225
|
+
for line in iter(process.stdout.readline, ''):
|
|
226
|
+
sys.stdout.write(line)
|
|
227
|
+
process.wait() # Wait for the process to finish
|
|
228
|
+
if process.returncode != 0:
|
|
229
|
+
print(f"Container exited with non-zero status code: {process.returncode}")
|
|
230
|
+
except KeyboardInterrupt:
|
|
231
|
+
pass
|
|
232
|
+
|
|
233
|
+
print(f"\nContainer '{CONTAINER_NAME}' finished execution.")
|
|
234
|
+
return True
|
|
235
|
+
except FileNotFoundError:
|
|
236
|
+
print("Error: Docker command not found. Is Docker installed and in your PATH?")
|
|
237
|
+
print("Please ensure Docker is installed and running.")
|
|
238
|
+
return False
|
|
239
|
+
except subprocess.CalledProcessError as e:
|
|
240
|
+
print(f"Error running Docker container: {e}")
|
|
241
|
+
return False
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
# Entry point
|
|
245
|
+
if __name__ == "__main__":
|
|
246
|
+
parser = argparse.ArgumentParser(
|
|
247
|
+
description="Run a Python script adding customizable read-only and writable paths.",
|
|
248
|
+
formatter_class=argparse.RawTextHelpFormatter,
|
|
249
|
+
epilog="""
|
|
250
|
+
Examples:
|
|
251
|
+
python utils/sandbox.py my_script.py -r /home/user/data:/opt/app/data -p 1234
|
|
252
|
+
python utils/sandbox.py another_script.py -w /tmp/output:/mnt/results
|
|
253
|
+
python utils/sandbox.py script_with_both.py -r /input:/app/in -w /output:/app/out -p 8082
|
|
254
|
+
""")
|
|
255
|
+
parser.add_argument(help="Path to the Python script to execute.", dest="script_to_run",
|
|
256
|
+
type=str)
|
|
257
|
+
parser.add_argument("-p", "--port", dest="port",
|
|
258
|
+
help="The starting port of the node(s) (each node uses 4 ports, consecutive port numbers)",
|
|
259
|
+
type=str, required=True)
|
|
260
|
+
parser.add_argument("-r", "--read-only", dest="read_only_folders",
|
|
261
|
+
help="One or multiple paths to mount as read-only. "
|
|
262
|
+
"Use a colon to separate multiple paths (e.g., /path/a:/path/b).",
|
|
263
|
+
type=str, default=None)
|
|
264
|
+
parser.add_argument("-w", "--writable", dest="writable_folders",
|
|
265
|
+
help="One or multiple paths to mount as writable. "
|
|
266
|
+
"Use a colon to separate multiple paths (e.g., /path/c:/path/d).",
|
|
267
|
+
type=str, default=None)
|
|
268
|
+
args = parser.parse_args()
|
|
269
|
+
|
|
270
|
+
if not args.script_to_run.endswith(".py"):
|
|
271
|
+
parser.error(f"The script '{args.script_to_run}' must be a Python file (e.g., ending with .py)")
|
|
272
|
+
script_to_run = args.script_to_run
|
|
273
|
+
if not int(args.port) > 0:
|
|
274
|
+
parser.error(f"Invalid port")
|
|
275
|
+
|
|
276
|
+
read_only_folders = None
|
|
277
|
+
if args.read_only_folders:
|
|
278
|
+
read_only_folders = args.read_only_folders.split(':')
|
|
279
|
+
writable_folders = None
|
|
280
|
+
if args.writable_folders:
|
|
281
|
+
writable_folders = args.writable_folders.split(':')
|
|
282
|
+
|
|
283
|
+
print("\n Running in sandbox...")
|
|
284
|
+
print(f"- Script to run: {script_to_run}")
|
|
285
|
+
print(f"- Starting port (+0, +1, +2, +3): {args.port}")
|
|
286
|
+
print(f"- Read only paths to mount (the UNaIVERSE code folder will be automatically mounted): {read_only_folders}")
|
|
287
|
+
print(f"- Writable paths to mount: {writable_folders}\n")
|
|
288
|
+
|
|
289
|
+
# Marking
|
|
290
|
+
os.environ["NODE_STARTING_PORT"] = args.port
|
|
291
|
+
|
|
292
|
+
# Running the sandbox and the script
|
|
293
|
+
sandbox(script_to_run, read_only_paths=read_only_folders, writable_paths=writable_folders)
|