rxon 1.0b0__tar.gz
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.
- rxon-1.0b0/LICENSE +21 -0
- rxon-1.0b0/PKG-INFO +118 -0
- rxon-1.0b0/README.md +92 -0
- rxon-1.0b0/pyproject.toml +66 -0
- rxon-1.0b0/setup.cfg +4 -0
- rxon-1.0b0/src/rxon/__init__.py +152 -0
- rxon-1.0b0/src/rxon/blob.py +51 -0
- rxon-1.0b0/src/rxon/constants.py +94 -0
- rxon-1.0b0/src/rxon/exceptions.py +30 -0
- rxon-1.0b0/src/rxon/models.py +114 -0
- rxon-1.0b0/src/rxon/py.typed +0 -0
- rxon-1.0b0/src/rxon/security.py +80 -0
- rxon-1.0b0/src/rxon/transports/__init__.py +0 -0
- rxon-1.0b0/src/rxon/transports/base.py +114 -0
- rxon-1.0b0/src/rxon/transports/factory.py +18 -0
- rxon-1.0b0/src/rxon/transports/http.py +228 -0
- rxon-1.0b0/src/rxon/transports/http_server.py +157 -0
- rxon-1.0b0/src/rxon/utils.py +19 -0
- rxon-1.0b0/src/rxon/validators.py +28 -0
- rxon-1.0b0/src/rxon.egg-info/PKG-INFO +118 -0
- rxon-1.0b0/src/rxon.egg-info/SOURCES.txt +27 -0
- rxon-1.0b0/src/rxon.egg-info/dependency_links.txt +1 -0
- rxon-1.0b0/src/rxon.egg-info/requires.txt +2 -0
- rxon-1.0b0/src/rxon.egg-info/top_level.txt +1 -0
- rxon-1.0b0/tests/test_blob.py +49 -0
- rxon-1.0b0/tests/test_factory.py +19 -0
- rxon-1.0b0/tests/test_integration.py +351 -0
- rxon-1.0b0/tests/test_models.py +56 -0
- rxon-1.0b0/tests/test_validators.py +29 -0
rxon-1.0b0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dmitrii Gagarin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
rxon-1.0b0/PKG-INFO
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rxon
|
|
3
|
+
Version: 1.0b0
|
|
4
|
+
Summary: RXON (Reverse Axon) - Lightweight Inter-node Reverse Communication Protocol.
|
|
5
|
+
Author-email: Dmitrii Gagarin <madgagarin@gmail.com>
|
|
6
|
+
Maintainer-email: Dmitrii Gagarin <madgagarin@gmail.com>
|
|
7
|
+
Project-URL: Homepage, https://github.com/madgagarin/rxon
|
|
8
|
+
Project-URL: Repository, https://github.com/madgagarin/rxon
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/madgagarin/rxon/issues
|
|
10
|
+
Keywords: rxon,hln,protocol,distributed-systems,holarchy,holon
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: System Administrators
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: aiohttp>=3.12
|
|
24
|
+
Requires-Dist: orjson>=3.10
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
# RXON (Reverse Axon) Protocol
|
|
28
|
+
|
|
29
|
+
[](https://opensource.org/licenses/MIT)
|
|
30
|
+
[](https://www.python.org/downloads/release/python-3110/)
|
|
31
|
+
[](https://peps.python.org/pep-0561/)
|
|
32
|
+
|
|
33
|
+
> **[RU](https://github.com/madgagarin/rxon/blob/main/README_RU.md)**
|
|
34
|
+
|
|
35
|
+
**RXON** (Reverse Axon) is a lightweight reverse-connection inter-service communication protocol designed for the **[HLN (Hierarchical Logic Network)](https://github.com/avtomatika-ai/hln)** architecture.
|
|
36
|
+
|
|
37
|
+
It serves as the "nervous system" for distributed multi-agent systems, connecting autonomous nodes (Holons) into a single hierarchical network.
|
|
38
|
+
|
|
39
|
+
## 🧬 The Biological Metaphor
|
|
40
|
+
|
|
41
|
+
The name **RXON** is derived from the biological term *Axon* (the nerve fiber). In classic networks, commands typically flow "top-down" (Push model). In RXON, the connection initiative always comes from the subordinate node (Worker/Shell) to the superior node (Orchestrator/Ghost). This is a "Reverse Axon" that grows from the bottom up, creating a channel through which commands subsequently descend.
|
|
42
|
+
|
|
43
|
+
## ✨ Key Features
|
|
44
|
+
|
|
45
|
+
- **Pluggable Transports**: Full abstraction from the network layer. The same code can run over HTTP, WebSocket, gRPC, or Tor.
|
|
46
|
+
- **Zero Dependency Core**: The protocol core has no external dependencies (standard transports use `aiohttp` and `orjson`).
|
|
47
|
+
- **Strictly Typed**: All messages (tasks, results, heartbeats) are defined via strictly typed models for maximum performance and correctness.
|
|
48
|
+
- **Blob Storage Native**: Built-in support for offloading heavy data via S3-compatible storage (`rxon.blob`).
|
|
49
|
+
|
|
50
|
+
## 🏗 Architecture
|
|
51
|
+
|
|
52
|
+
The protocol is divided into two main interfaces:
|
|
53
|
+
|
|
54
|
+
1. **Transport (Worker side)**: Interface for initiating connections, retrieving tasks, and sending results.
|
|
55
|
+
2. **Listener (Orchestrator side)**: Interface for accepting incoming connections and routing messages to the orchestration engine.
|
|
56
|
+
|
|
57
|
+
### Usage Example (Worker side)
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from rxon import create_transport, WorkerRegistration
|
|
61
|
+
|
|
62
|
+
# 1. Create transport (automatically selects HttpTransport based on URL scheme)
|
|
63
|
+
transport = create_transport(
|
|
64
|
+
url="https://orchestrator.local",
|
|
65
|
+
worker_id="gpu-01",
|
|
66
|
+
token="secret-token"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
await transport.connect()
|
|
70
|
+
|
|
71
|
+
# 2. Register
|
|
72
|
+
await transport.register(reg_payload)
|
|
73
|
+
|
|
74
|
+
# 3. Poll for tasks
|
|
75
|
+
task = await transport.poll_task(timeout=30)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Usage Example (Orchestrator side)
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from rxon import HttpListener, TaskPayload
|
|
82
|
+
|
|
83
|
+
async def my_handler(message_type, payload, context):
|
|
84
|
+
if message_type == "poll":
|
|
85
|
+
# Task dispatch logic
|
|
86
|
+
return TaskPayload(...)
|
|
87
|
+
return True
|
|
88
|
+
|
|
89
|
+
# Listener attaches to a web application or starts its own server
|
|
90
|
+
listener = HttpListener(app)
|
|
91
|
+
await listener.start(handler=my_handler)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## 📦 Package Structure
|
|
95
|
+
|
|
96
|
+
- **`rxon.models`**: DTOs for registrations, tasks, heartbeats, and results.
|
|
97
|
+
- **`rxon.constants`**: Standardized error codes (TIMEOUT, RESOURCE_EXHAUSTED, etc.) and API endpoints.
|
|
98
|
+
- **`rxon.transports`**: Abstract base classes and implementations (HTTP, WebSocket).
|
|
99
|
+
- **`rxon.blob`**: Unified interface for blob storage operations (S3 URI parsing, hashing).
|
|
100
|
+
- **`rxon.security`**: Helpers for mTLS and access tokens.
|
|
101
|
+
|
|
102
|
+
## 🚀 Installation
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
pip install rxon
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
For developers (local):
|
|
109
|
+
```bash
|
|
110
|
+
pip install -e packages/rxon
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## 📜 License
|
|
114
|
+
|
|
115
|
+
The project is distributed under the MIT License.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
*Mantra: "The RXON is the medium for the Ghost."*
|
rxon-1.0b0/README.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# RXON (Reverse Axon) Protocol
|
|
2
|
+
|
|
3
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
|
+
[](https://www.python.org/downloads/release/python-3110/)
|
|
5
|
+
[](https://peps.python.org/pep-0561/)
|
|
6
|
+
|
|
7
|
+
> **[RU](https://github.com/madgagarin/rxon/blob/main/README_RU.md)**
|
|
8
|
+
|
|
9
|
+
**RXON** (Reverse Axon) is a lightweight reverse-connection inter-service communication protocol designed for the **[HLN (Hierarchical Logic Network)](https://github.com/avtomatika-ai/hln)** architecture.
|
|
10
|
+
|
|
11
|
+
It serves as the "nervous system" for distributed multi-agent systems, connecting autonomous nodes (Holons) into a single hierarchical network.
|
|
12
|
+
|
|
13
|
+
## 🧬 The Biological Metaphor
|
|
14
|
+
|
|
15
|
+
The name **RXON** is derived from the biological term *Axon* (the nerve fiber). In classic networks, commands typically flow "top-down" (Push model). In RXON, the connection initiative always comes from the subordinate node (Worker/Shell) to the superior node (Orchestrator/Ghost). This is a "Reverse Axon" that grows from the bottom up, creating a channel through which commands subsequently descend.
|
|
16
|
+
|
|
17
|
+
## ✨ Key Features
|
|
18
|
+
|
|
19
|
+
- **Pluggable Transports**: Full abstraction from the network layer. The same code can run over HTTP, WebSocket, gRPC, or Tor.
|
|
20
|
+
- **Zero Dependency Core**: The protocol core has no external dependencies (standard transports use `aiohttp` and `orjson`).
|
|
21
|
+
- **Strictly Typed**: All messages (tasks, results, heartbeats) are defined via strictly typed models for maximum performance and correctness.
|
|
22
|
+
- **Blob Storage Native**: Built-in support for offloading heavy data via S3-compatible storage (`rxon.blob`).
|
|
23
|
+
|
|
24
|
+
## 🏗 Architecture
|
|
25
|
+
|
|
26
|
+
The protocol is divided into two main interfaces:
|
|
27
|
+
|
|
28
|
+
1. **Transport (Worker side)**: Interface for initiating connections, retrieving tasks, and sending results.
|
|
29
|
+
2. **Listener (Orchestrator side)**: Interface for accepting incoming connections and routing messages to the orchestration engine.
|
|
30
|
+
|
|
31
|
+
### Usage Example (Worker side)
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from rxon import create_transport, WorkerRegistration
|
|
35
|
+
|
|
36
|
+
# 1. Create transport (automatically selects HttpTransport based on URL scheme)
|
|
37
|
+
transport = create_transport(
|
|
38
|
+
url="https://orchestrator.local",
|
|
39
|
+
worker_id="gpu-01",
|
|
40
|
+
token="secret-token"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
await transport.connect()
|
|
44
|
+
|
|
45
|
+
# 2. Register
|
|
46
|
+
await transport.register(reg_payload)
|
|
47
|
+
|
|
48
|
+
# 3. Poll for tasks
|
|
49
|
+
task = await transport.poll_task(timeout=30)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Usage Example (Orchestrator side)
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from rxon import HttpListener, TaskPayload
|
|
56
|
+
|
|
57
|
+
async def my_handler(message_type, payload, context):
|
|
58
|
+
if message_type == "poll":
|
|
59
|
+
# Task dispatch logic
|
|
60
|
+
return TaskPayload(...)
|
|
61
|
+
return True
|
|
62
|
+
|
|
63
|
+
# Listener attaches to a web application or starts its own server
|
|
64
|
+
listener = HttpListener(app)
|
|
65
|
+
await listener.start(handler=my_handler)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## 📦 Package Structure
|
|
69
|
+
|
|
70
|
+
- **`rxon.models`**: DTOs for registrations, tasks, heartbeats, and results.
|
|
71
|
+
- **`rxon.constants`**: Standardized error codes (TIMEOUT, RESOURCE_EXHAUSTED, etc.) and API endpoints.
|
|
72
|
+
- **`rxon.transports`**: Abstract base classes and implementations (HTTP, WebSocket).
|
|
73
|
+
- **`rxon.blob`**: Unified interface for blob storage operations (S3 URI parsing, hashing).
|
|
74
|
+
- **`rxon.security`**: Helpers for mTLS and access tokens.
|
|
75
|
+
|
|
76
|
+
## 🚀 Installation
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pip install rxon
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
For developers (local):
|
|
83
|
+
```bash
|
|
84
|
+
pip install -e packages/rxon
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## 📜 License
|
|
88
|
+
|
|
89
|
+
The project is distributed under the MIT License.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
*Mantra: "The RXON is the medium for the Ghost."*
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "rxon"
|
|
7
|
+
version = "1.0b0"
|
|
8
|
+
description = "RXON (Reverse Axon) - Lightweight Inter-node Reverse Communication Protocol."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
authors = [
|
|
12
|
+
{name = "Dmitrii Gagarin", email = "madgagarin@gmail.com"},
|
|
13
|
+
]
|
|
14
|
+
maintainers = [
|
|
15
|
+
{name = "Dmitrii Gagarin", email = "madgagarin@gmail.com"},
|
|
16
|
+
]
|
|
17
|
+
keywords = ["rxon", "hln", "protocol", "distributed-systems", "holarchy", "holon"]
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Development Status :: 4 - Beta",
|
|
20
|
+
"Intended Audience :: Developers",
|
|
21
|
+
"Intended Audience :: System Administrators",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"License :: OSI Approved :: MIT License",
|
|
26
|
+
"Operating System :: OS Independent",
|
|
27
|
+
"Typing :: Typed",
|
|
28
|
+
]
|
|
29
|
+
dependencies = [
|
|
30
|
+
"aiohttp>=3.12",
|
|
31
|
+
"orjson>=3.10",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
"Homepage" = "https://github.com/madgagarin/rxon"
|
|
36
|
+
"Repository" = "https://github.com/madgagarin/rxon"
|
|
37
|
+
"Bug Tracker" = "https://github.com/madgagarin/rxon/issues"
|
|
38
|
+
|
|
39
|
+
[tool.setuptools.packages.find]
|
|
40
|
+
where = ["src"]
|
|
41
|
+
|
|
42
|
+
[tool.setuptools.package-data]
|
|
43
|
+
rxon = ["py.typed"]
|
|
44
|
+
|
|
45
|
+
[tool.ruff]
|
|
46
|
+
src = ["src"]
|
|
47
|
+
target-version = "py311"
|
|
48
|
+
line-length = 120
|
|
49
|
+
|
|
50
|
+
[tool.ruff.lint]
|
|
51
|
+
select = ["E", "W", "F", "I", "B", "A", "SIM", "C4"]
|
|
52
|
+
|
|
53
|
+
[tool.ruff.format]
|
|
54
|
+
quote-style = "double"
|
|
55
|
+
|
|
56
|
+
[tool.pytest.ini_options]
|
|
57
|
+
asyncio_mode = "auto"
|
|
58
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
59
|
+
|
|
60
|
+
[dependency-groups]
|
|
61
|
+
dev = [
|
|
62
|
+
"mypy>=1.19.1",
|
|
63
|
+
"pytest>=9.0.2",
|
|
64
|
+
"pytest-asyncio>=1.3.0",
|
|
65
|
+
"pytest-cov>=7.0.0",
|
|
66
|
+
]
|
rxon-1.0b0/setup.cfg
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
2
|
+
|
|
3
|
+
from .blob import RXON_BLOB_SCHEME, calculate_config_hash, parse_uri
|
|
4
|
+
from .constants import (
|
|
5
|
+
AUTH_HEADER_CLIENT,
|
|
6
|
+
AUTH_HEADER_WORKER,
|
|
7
|
+
COMMAND_CANCEL_TASK,
|
|
8
|
+
ENDPOINT_TASK_NEXT,
|
|
9
|
+
ENDPOINT_TASK_RESULT,
|
|
10
|
+
ENDPOINT_WORKER_HEARTBEAT,
|
|
11
|
+
ENDPOINT_WORKER_REGISTER,
|
|
12
|
+
ERROR_CODE_DEPENDENCY,
|
|
13
|
+
ERROR_CODE_INTEGRITY_MISMATCH,
|
|
14
|
+
ERROR_CODE_INTERNAL,
|
|
15
|
+
ERROR_CODE_INVALID_INPUT,
|
|
16
|
+
ERROR_CODE_PERMANENT,
|
|
17
|
+
ERROR_CODE_RESOURCE_EXHAUSTED,
|
|
18
|
+
ERROR_CODE_SECURITY,
|
|
19
|
+
ERROR_CODE_TIMEOUT,
|
|
20
|
+
ERROR_CODE_TRANSIENT,
|
|
21
|
+
JOB_STATUS_CANCELLED,
|
|
22
|
+
JOB_STATUS_ERROR,
|
|
23
|
+
JOB_STATUS_FAILED,
|
|
24
|
+
JOB_STATUS_FINISHED,
|
|
25
|
+
JOB_STATUS_PENDING,
|
|
26
|
+
JOB_STATUS_QUARANTINED,
|
|
27
|
+
JOB_STATUS_RUNNING,
|
|
28
|
+
JOB_STATUS_WAITING_FOR_HUMAN,
|
|
29
|
+
JOB_STATUS_WAITING_FOR_PARALLEL,
|
|
30
|
+
JOB_STATUS_WAITING_FOR_WORKER,
|
|
31
|
+
MSG_TYPE_PROGRESS,
|
|
32
|
+
PROTOCOL_VERSION,
|
|
33
|
+
STS_TOKEN_ENDPOINT,
|
|
34
|
+
TASK_STATUS_CANCELLED,
|
|
35
|
+
TASK_STATUS_FAILURE,
|
|
36
|
+
TASK_STATUS_SUCCESS,
|
|
37
|
+
WORKER_API_PREFIX,
|
|
38
|
+
WS_ENDPOINT,
|
|
39
|
+
)
|
|
40
|
+
from .exceptions import (
|
|
41
|
+
IntegrityError,
|
|
42
|
+
ParamValidationError,
|
|
43
|
+
RXONProtocolError,
|
|
44
|
+
S3ConfigMismatchError,
|
|
45
|
+
)
|
|
46
|
+
from .models import (
|
|
47
|
+
FileMetadata,
|
|
48
|
+
GPUInfo,
|
|
49
|
+
Heartbeat,
|
|
50
|
+
InstalledModel,
|
|
51
|
+
ProgressUpdatePayload,
|
|
52
|
+
Resources,
|
|
53
|
+
TaskError,
|
|
54
|
+
TaskPayload,
|
|
55
|
+
TaskResult,
|
|
56
|
+
TokenResponse,
|
|
57
|
+
WorkerCapabilities,
|
|
58
|
+
WorkerCommand,
|
|
59
|
+
WorkerRegistration,
|
|
60
|
+
)
|
|
61
|
+
from .security import (
|
|
62
|
+
create_client_ssl_context,
|
|
63
|
+
create_server_ssl_context,
|
|
64
|
+
extract_cert_identity,
|
|
65
|
+
)
|
|
66
|
+
from .transports.base import Listener, Transport
|
|
67
|
+
from .transports.factory import create_transport
|
|
68
|
+
from .transports.http import HttpTransport
|
|
69
|
+
from .transports.http_server import HttpListener
|
|
70
|
+
from .utils import to_dict
|
|
71
|
+
from .validators import is_valid_identifier, validate_identifier
|
|
72
|
+
|
|
73
|
+
__all__ = [
|
|
74
|
+
# Blob
|
|
75
|
+
"RXON_BLOB_SCHEME",
|
|
76
|
+
"calculate_config_hash",
|
|
77
|
+
"parse_uri",
|
|
78
|
+
# Constants
|
|
79
|
+
"AUTH_HEADER_CLIENT",
|
|
80
|
+
"AUTH_HEADER_WORKER",
|
|
81
|
+
"COMMAND_CANCEL_TASK",
|
|
82
|
+
"ENDPOINT_TASK_NEXT",
|
|
83
|
+
"ENDPOINT_TASK_RESULT",
|
|
84
|
+
"ENDPOINT_WORKER_HEARTBEAT",
|
|
85
|
+
"ENDPOINT_WORKER_REGISTER",
|
|
86
|
+
"ERROR_CODE_DEPENDENCY",
|
|
87
|
+
"ERROR_CODE_INTEGRITY_MISMATCH",
|
|
88
|
+
"ERROR_CODE_INTERNAL",
|
|
89
|
+
"ERROR_CODE_INVALID_INPUT",
|
|
90
|
+
"ERROR_CODE_PERMANENT",
|
|
91
|
+
"ERROR_CODE_RESOURCE_EXHAUSTED",
|
|
92
|
+
"ERROR_CODE_SECURITY",
|
|
93
|
+
"ERROR_CODE_TIMEOUT",
|
|
94
|
+
"ERROR_CODE_TRANSIENT",
|
|
95
|
+
"JOB_STATUS_CANCELLED",
|
|
96
|
+
"JOB_STATUS_ERROR",
|
|
97
|
+
"JOB_STATUS_FAILED",
|
|
98
|
+
"JOB_STATUS_FINISHED",
|
|
99
|
+
"JOB_STATUS_PENDING",
|
|
100
|
+
"JOB_STATUS_QUARANTINED",
|
|
101
|
+
"JOB_STATUS_RUNNING",
|
|
102
|
+
"JOB_STATUS_WAITING_FOR_HUMAN",
|
|
103
|
+
"JOB_STATUS_WAITING_FOR_PARALLEL",
|
|
104
|
+
"JOB_STATUS_WAITING_FOR_WORKER",
|
|
105
|
+
"MSG_TYPE_PROGRESS",
|
|
106
|
+
"PROTOCOL_VERSION",
|
|
107
|
+
"STS_TOKEN_ENDPOINT",
|
|
108
|
+
"TASK_STATUS_CANCELLED",
|
|
109
|
+
"TASK_STATUS_FAILURE",
|
|
110
|
+
"TASK_STATUS_SUCCESS",
|
|
111
|
+
"WORKER_API_PREFIX",
|
|
112
|
+
"WS_ENDPOINT",
|
|
113
|
+
# Exceptions
|
|
114
|
+
"IntegrityError",
|
|
115
|
+
"ParamValidationError",
|
|
116
|
+
"RXONProtocolError",
|
|
117
|
+
"S3ConfigMismatchError",
|
|
118
|
+
# Models
|
|
119
|
+
"FileMetadata",
|
|
120
|
+
"GPUInfo",
|
|
121
|
+
"Heartbeat",
|
|
122
|
+
"InstalledModel",
|
|
123
|
+
"ProgressUpdatePayload",
|
|
124
|
+
"Resources",
|
|
125
|
+
"TaskError",
|
|
126
|
+
"TaskPayload",
|
|
127
|
+
"TaskResult",
|
|
128
|
+
"TokenResponse",
|
|
129
|
+
"WorkerCapabilities",
|
|
130
|
+
"WorkerCommand",
|
|
131
|
+
"WorkerRegistration",
|
|
132
|
+
# Security
|
|
133
|
+
"create_client_ssl_context",
|
|
134
|
+
"create_server_ssl_context",
|
|
135
|
+
"extract_cert_identity",
|
|
136
|
+
# Transports
|
|
137
|
+
"Listener",
|
|
138
|
+
"Transport",
|
|
139
|
+
"create_transport",
|
|
140
|
+
"HttpTransport",
|
|
141
|
+
"HttpListener",
|
|
142
|
+
# Utils
|
|
143
|
+
"to_dict",
|
|
144
|
+
# Validators
|
|
145
|
+
"is_valid_identifier",
|
|
146
|
+
"validate_identifier",
|
|
147
|
+
]
|
|
148
|
+
|
|
149
|
+
try:
|
|
150
|
+
__version__ = version("rxon")
|
|
151
|
+
except PackageNotFoundError:
|
|
152
|
+
__version__ = "unknown"
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from hashlib import sha256
|
|
2
|
+
from typing import Tuple
|
|
3
|
+
from urllib.parse import urlparse
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"RXON_BLOB_SCHEME",
|
|
7
|
+
"calculate_config_hash",
|
|
8
|
+
"parse_uri",
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
RXON_BLOB_SCHEME = "s3"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def calculate_config_hash(endpoint: str | None, access_key: str | None, bucket: str | None) -> str | None:
|
|
15
|
+
"""
|
|
16
|
+
Calculates a consistent hash of the Blob/S3 configuration.
|
|
17
|
+
Used to ensure Workers and Orchestrators are talking to the same storage.
|
|
18
|
+
Uses '|' as separator.
|
|
19
|
+
"""
|
|
20
|
+
if not endpoint or not access_key or not bucket:
|
|
21
|
+
return None
|
|
22
|
+
|
|
23
|
+
config_str = f"{endpoint}|{access_key}|{bucket}"
|
|
24
|
+
return sha256(config_str.encode()).hexdigest()[:16]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def parse_uri(uri: str, default_bucket: str | None = None, prefix: str = "") -> Tuple[str, str, bool]:
|
|
28
|
+
"""
|
|
29
|
+
Parses a Blob/S3 URI or relative path into (bucket, key, is_directory).
|
|
30
|
+
Protocol: s3://bucket/key
|
|
31
|
+
|
|
32
|
+
:param uri: Full URI (s3://bucket/key) or relative path (key)
|
|
33
|
+
:param default_bucket: Bucket to use if URI is relative
|
|
34
|
+
:param prefix: Optional prefix to prepend to relative paths
|
|
35
|
+
:return: (bucket, key, is_directory)
|
|
36
|
+
:raises ValueError: If URI format is invalid or bucket is missing
|
|
37
|
+
"""
|
|
38
|
+
is_dir = uri.endswith("/")
|
|
39
|
+
|
|
40
|
+
if uri.startswith(f"{RXON_BLOB_SCHEME}://"):
|
|
41
|
+
parsed = urlparse(uri)
|
|
42
|
+
bucket = parsed.netloc
|
|
43
|
+
key = parsed.path.lstrip("/")
|
|
44
|
+
return bucket, key, is_dir
|
|
45
|
+
else:
|
|
46
|
+
if not default_bucket:
|
|
47
|
+
raise ValueError(f"Cannot parse relative path '{uri}' without a default bucket.")
|
|
48
|
+
|
|
49
|
+
clean_path = uri.lstrip("/")
|
|
50
|
+
key = f"{prefix}{clean_path}" if prefix else clean_path
|
|
51
|
+
return default_bucket, key, is_dir
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Centralized constants for the RXON protocol.
|
|
3
|
+
Shared between Orchestrator and Worker SDK.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"AUTH_HEADER_CLIENT",
|
|
8
|
+
"AUTH_HEADER_WORKER",
|
|
9
|
+
"ERROR_CODE_TRANSIENT",
|
|
10
|
+
"ERROR_CODE_PERMANENT",
|
|
11
|
+
"ERROR_CODE_INVALID_INPUT",
|
|
12
|
+
"ERROR_CODE_INTEGRITY_MISMATCH",
|
|
13
|
+
"ERROR_CODE_TIMEOUT",
|
|
14
|
+
"ERROR_CODE_RESOURCE_EXHAUSTED",
|
|
15
|
+
"ERROR_CODE_DEPENDENCY",
|
|
16
|
+
"ERROR_CODE_SECURITY",
|
|
17
|
+
"ERROR_CODE_INTERNAL",
|
|
18
|
+
"TASK_STATUS_SUCCESS",
|
|
19
|
+
"TASK_STATUS_FAILURE",
|
|
20
|
+
"TASK_STATUS_CANCELLED",
|
|
21
|
+
"COMMAND_CANCEL_TASK",
|
|
22
|
+
"MSG_TYPE_PROGRESS",
|
|
23
|
+
"STS_TOKEN_ENDPOINT",
|
|
24
|
+
"WS_ENDPOINT",
|
|
25
|
+
"ENDPOINT_WORKER_REGISTER",
|
|
26
|
+
"ENDPOINT_TASK_NEXT",
|
|
27
|
+
"ENDPOINT_TASK_RESULT",
|
|
28
|
+
"ENDPOINT_WORKER_HEARTBEAT",
|
|
29
|
+
"JOB_STATUS_PENDING",
|
|
30
|
+
"JOB_STATUS_WAITING_FOR_WORKER",
|
|
31
|
+
"JOB_STATUS_RUNNING",
|
|
32
|
+
"JOB_STATUS_FINISHED",
|
|
33
|
+
"JOB_STATUS_FAILED",
|
|
34
|
+
"JOB_STATUS_ERROR",
|
|
35
|
+
"JOB_STATUS_QUARANTINED",
|
|
36
|
+
"JOB_STATUS_CANCELLED",
|
|
37
|
+
"JOB_STATUS_WAITING_FOR_HUMAN",
|
|
38
|
+
"JOB_STATUS_WAITING_FOR_PARALLEL",
|
|
39
|
+
"WORKER_API_PREFIX",
|
|
40
|
+
"PROTOCOL_VERSION",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
# --- Protocol Metadata ---
|
|
44
|
+
PROTOCOL_VERSION = "1.0"
|
|
45
|
+
|
|
46
|
+
# --- Auth Headers ---
|
|
47
|
+
AUTH_HEADER_CLIENT = "X-Client-Token"
|
|
48
|
+
AUTH_HEADER_WORKER = "X-Worker-Token"
|
|
49
|
+
|
|
50
|
+
# --- Error Codes ---
|
|
51
|
+
# Error codes returned by workers in the result payload
|
|
52
|
+
ERROR_CODE_TRANSIENT = "TRANSIENT_ERROR"
|
|
53
|
+
ERROR_CODE_PERMANENT = "PERMANENT_ERROR"
|
|
54
|
+
ERROR_CODE_INVALID_INPUT = "INVALID_INPUT_ERROR"
|
|
55
|
+
ERROR_CODE_INTEGRITY_MISMATCH = "INTEGRITY_MISMATCH_ERROR"
|
|
56
|
+
ERROR_CODE_TIMEOUT = "TIMEOUT_ERROR"
|
|
57
|
+
ERROR_CODE_RESOURCE_EXHAUSTED = "RESOURCE_EXHAUSTED_ERROR"
|
|
58
|
+
ERROR_CODE_DEPENDENCY = "DEPENDENCY_ERROR"
|
|
59
|
+
ERROR_CODE_SECURITY = "SECURITY_ERROR"
|
|
60
|
+
ERROR_CODE_INTERNAL = "INTERNAL_ERROR"
|
|
61
|
+
|
|
62
|
+
# --- Task Statuses ---
|
|
63
|
+
# Standard statuses for task results
|
|
64
|
+
TASK_STATUS_SUCCESS = "success"
|
|
65
|
+
TASK_STATUS_FAILURE = "failure"
|
|
66
|
+
TASK_STATUS_CANCELLED = "cancelled"
|
|
67
|
+
|
|
68
|
+
# --- Commands (WebSocket) ---
|
|
69
|
+
COMMAND_CANCEL_TASK = "cancel_task"
|
|
70
|
+
MSG_TYPE_PROGRESS = "progress_update"
|
|
71
|
+
|
|
72
|
+
# --- Endpoints ---
|
|
73
|
+
WORKER_API_PREFIX = "/_worker"
|
|
74
|
+
|
|
75
|
+
STS_TOKEN_ENDPOINT = f"{WORKER_API_PREFIX}/auth/token"
|
|
76
|
+
WS_ENDPOINT = f"{WORKER_API_PREFIX}/ws"
|
|
77
|
+
|
|
78
|
+
# Routes for REST API interaction
|
|
79
|
+
ENDPOINT_WORKER_REGISTER = f"{WORKER_API_PREFIX}/workers/register"
|
|
80
|
+
ENDPOINT_TASK_NEXT = f"{WORKER_API_PREFIX}/workers/{{worker_id}}/tasks/next"
|
|
81
|
+
ENDPOINT_TASK_RESULT = f"{WORKER_API_PREFIX}/tasks/result"
|
|
82
|
+
ENDPOINT_WORKER_HEARTBEAT = f"{WORKER_API_PREFIX}/workers/{{worker_id}}"
|
|
83
|
+
|
|
84
|
+
# --- Job Statuses ---
|
|
85
|
+
JOB_STATUS_PENDING = "pending"
|
|
86
|
+
JOB_STATUS_WAITING_FOR_WORKER = "waiting_for_worker"
|
|
87
|
+
JOB_STATUS_RUNNING = "running"
|
|
88
|
+
JOB_STATUS_FINISHED = "finished"
|
|
89
|
+
JOB_STATUS_FAILED = "failed"
|
|
90
|
+
JOB_STATUS_ERROR = "error"
|
|
91
|
+
JOB_STATUS_QUARANTINED = "quarantined"
|
|
92
|
+
JOB_STATUS_CANCELLED = "cancelled"
|
|
93
|
+
JOB_STATUS_WAITING_FOR_HUMAN = "waiting_for_human"
|
|
94
|
+
JOB_STATUS_WAITING_FOR_PARALLEL = "waiting_for_parallel_tasks"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
"RXONProtocolError",
|
|
3
|
+
"S3ConfigMismatchError",
|
|
4
|
+
"IntegrityError",
|
|
5
|
+
"ParamValidationError",
|
|
6
|
+
]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class RXONProtocolError(Exception):
|
|
10
|
+
"""Base exception for all protocol-related errors."""
|
|
11
|
+
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class S3ConfigMismatchError(RXONProtocolError):
|
|
16
|
+
"""Raised when Worker and Orchestrator S3 configurations do not match."""
|
|
17
|
+
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class IntegrityError(RXONProtocolError):
|
|
22
|
+
"""Raised when file integrity check (size/hash) fails."""
|
|
23
|
+
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ParamValidationError(RXONProtocolError):
|
|
28
|
+
"""Raised when task parameters fail validation."""
|
|
29
|
+
|
|
30
|
+
pass
|