nexus-worker 0.1.0__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.
- nexus_worker-0.1.0/LICENSE +21 -0
- nexus_worker-0.1.0/PKG-INFO +130 -0
- nexus_worker-0.1.0/README.md +111 -0
- nexus_worker-0.1.0/nexus_worker/__init__.py +2 -0
- nexus_worker-0.1.0/nexus_worker/cli.py +96 -0
- nexus_worker-0.1.0/nexus_worker/client.py +174 -0
- nexus_worker-0.1.0/nexus_worker/engine.py +146 -0
- nexus_worker-0.1.0/nexus_worker/hardware.py +56 -0
- nexus_worker-0.1.0/nexus_worker.egg-info/PKG-INFO +130 -0
- nexus_worker-0.1.0/nexus_worker.egg-info/SOURCES.txt +15 -0
- nexus_worker-0.1.0/nexus_worker.egg-info/dependency_links.txt +1 -0
- nexus_worker-0.1.0/nexus_worker.egg-info/entry_points.txt +2 -0
- nexus_worker-0.1.0/nexus_worker.egg-info/requires.txt +4 -0
- nexus_worker-0.1.0/nexus_worker.egg-info/top_level.txt +1 -0
- nexus_worker-0.1.0/pyproject.toml +31 -0
- nexus_worker-0.1.0/setup.cfg +4 -0
- nexus_worker-0.1.0/setup.py +18 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 NexusCompute Network
|
|
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.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nexus-worker
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Monetize your GPU by serving open-source AI inference in the NexusCompute decentralized network.
|
|
5
|
+
Author-email: NexusCompute Network <providers@nexuscompute.ai>
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
11
|
+
Requires-Python: >=3.9
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: httpx>=0.27.0
|
|
15
|
+
Requires-Dist: websockets>=12.0
|
|
16
|
+
Requires-Dist: click>=8.1.7
|
|
17
|
+
Requires-Dist: rich>=13.7.0
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# ⚡ NexusWorker (`nexus-worker`)
|
|
21
|
+
|
|
22
|
+
[](LICENSE)
|
|
23
|
+
[](https://www.python.org/downloads/)
|
|
24
|
+
[](https://nexuscompute.ai)
|
|
25
|
+
|
|
26
|
+
**NexusWorker** is the open-source provider daemon for the **NexusCompute** decentralized AI inference network.
|
|
27
|
+
|
|
28
|
+
Run this lightweight daemon on your PC, mining rig, or server to monetize your idle graphics cards (NVIDIA, AMD, Apple Silicon) by serving inference tokens for open-source LLMs (Llama 3, DeepSeek-R1, Mistral).
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 🔒 Security & Privacy (Outbound-Only)
|
|
33
|
+
|
|
34
|
+
NexusWorker was designed from the ground up for strict security:
|
|
35
|
+
|
|
36
|
+
* **Zero Inbound Ports**: The daemon initiates an **outbound-only** encrypted WebSocket connection to the NexusCompute coordinator. You **never** need to configure port forwarding, open firewall rules, or expose a public IP.
|
|
37
|
+
* **Completely Sandboxed**: The worker only communicates with your local inference engine (e.g. Ollama or vLLM) over localhost (`127.0.0.1`).
|
|
38
|
+
* **100% Open Source**: Full transparency—inspect every line of code before running.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 🚀 Quickstart
|
|
43
|
+
|
|
44
|
+
### 1. Requirements
|
|
45
|
+
|
|
46
|
+
* **OS**: Linux, Windows, or macOS
|
|
47
|
+
* **Python**: 3.9 or higher
|
|
48
|
+
* **Inference Engine**: [Ollama](https://ollama.ai) (recommended for 1-click setup) or [vLLM](https://github.com/vllm-project/vllm).
|
|
49
|
+
* **Hardware**: Any modern GPU with 6GB+ VRAM (e.g. RTX 3060/3080/4090, Apple M-series), or CPU in emulation mode.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
### 2. Installation
|
|
54
|
+
|
|
55
|
+
Clone and install in a Python virtual environment:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
git clone https://github.com/nexus-compute-ai/nexus-worker.git
|
|
59
|
+
cd nexus-worker
|
|
60
|
+
|
|
61
|
+
# Create and activate virtual environment
|
|
62
|
+
python -m venv .venv
|
|
63
|
+
# On Windows:
|
|
64
|
+
.\.venv\Scripts\activate
|
|
65
|
+
# On Linux/macOS:
|
|
66
|
+
source .venv/bin/activate
|
|
67
|
+
|
|
68
|
+
# Install package
|
|
69
|
+
pip install -e .
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
### 3. Check Detected Hardware
|
|
75
|
+
|
|
76
|
+
Verify that your GPU and local models are recognized:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
nexus-worker info
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Example output:
|
|
83
|
+
```text
|
|
84
|
+
============================================================
|
|
85
|
+
NEXUSCOMPUTE HARDWARE DETECTOR
|
|
86
|
+
============================================================
|
|
87
|
+
• GPU: NVIDIA GeForce RTX 4090
|
|
88
|
+
• VRAM: 24,576 MB
|
|
89
|
+
• Backend Engine: Ollama (http://127.0.0.1:11434)
|
|
90
|
+
• Discovered Models:
|
|
91
|
+
- llama3:8b
|
|
92
|
+
- deepseek-r1:7b
|
|
93
|
+
- mistral:7b
|
|
94
|
+
============================================================
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
### 4. Connect to Network & Earn
|
|
100
|
+
|
|
101
|
+
Get your node token from the **Provider Dashboard** on NexusCompute, then start the daemon:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
nexus-worker start --token nx-node-YOUR_TOKEN_HERE
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
#### CLI Options:
|
|
108
|
+
* `--token TEXT`: Your unique node authorization token *(required)*.
|
|
109
|
+
* `--coordinator TEXT`: Coordinator WebSocket URL (default: `ws://127.0.0.1:8000/api/v1/ws/worker`).
|
|
110
|
+
* `--engine [auto|ollama|vllm|emulate]`: Local inference engine (default: `auto`).
|
|
111
|
+
* `--emulate`: Run in emulation mode for development without downloading weights.
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# Example with custom coordinator and auto-discovery
|
|
115
|
+
nexus-worker start --token nx-node-0a0a3b29ed4e323af354549a6681821a1b75157e
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## 🛠️ Supported Engines
|
|
121
|
+
|
|
122
|
+
1. **Ollama (Recommended)**: Pull models via `ollama pull llama3:8b`. NexusWorker will auto-detect active models and advertise them to the coordinator.
|
|
123
|
+
2. **vLLM (High-Throughput)**: Launch with OpenAI-compatible API (`--port 8000`).
|
|
124
|
+
3. **Emulation Engine**: Built-in mock tokenizer and streaming generator for rapid testing.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 📄 License
|
|
129
|
+
|
|
130
|
+
Distributed under the **MIT License**. See [LICENSE](LICENSE) for more information.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# ⚡ NexusWorker (`nexus-worker`)
|
|
2
|
+
|
|
3
|
+
[](LICENSE)
|
|
4
|
+
[](https://www.python.org/downloads/)
|
|
5
|
+
[](https://nexuscompute.ai)
|
|
6
|
+
|
|
7
|
+
**NexusWorker** is the open-source provider daemon for the **NexusCompute** decentralized AI inference network.
|
|
8
|
+
|
|
9
|
+
Run this lightweight daemon on your PC, mining rig, or server to monetize your idle graphics cards (NVIDIA, AMD, Apple Silicon) by serving inference tokens for open-source LLMs (Llama 3, DeepSeek-R1, Mistral).
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 🔒 Security & Privacy (Outbound-Only)
|
|
14
|
+
|
|
15
|
+
NexusWorker was designed from the ground up for strict security:
|
|
16
|
+
|
|
17
|
+
* **Zero Inbound Ports**: The daemon initiates an **outbound-only** encrypted WebSocket connection to the NexusCompute coordinator. You **never** need to configure port forwarding, open firewall rules, or expose a public IP.
|
|
18
|
+
* **Completely Sandboxed**: The worker only communicates with your local inference engine (e.g. Ollama or vLLM) over localhost (`127.0.0.1`).
|
|
19
|
+
* **100% Open Source**: Full transparency—inspect every line of code before running.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 🚀 Quickstart
|
|
24
|
+
|
|
25
|
+
### 1. Requirements
|
|
26
|
+
|
|
27
|
+
* **OS**: Linux, Windows, or macOS
|
|
28
|
+
* **Python**: 3.9 or higher
|
|
29
|
+
* **Inference Engine**: [Ollama](https://ollama.ai) (recommended for 1-click setup) or [vLLM](https://github.com/vllm-project/vllm).
|
|
30
|
+
* **Hardware**: Any modern GPU with 6GB+ VRAM (e.g. RTX 3060/3080/4090, Apple M-series), or CPU in emulation mode.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
### 2. Installation
|
|
35
|
+
|
|
36
|
+
Clone and install in a Python virtual environment:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
git clone https://github.com/nexus-compute-ai/nexus-worker.git
|
|
40
|
+
cd nexus-worker
|
|
41
|
+
|
|
42
|
+
# Create and activate virtual environment
|
|
43
|
+
python -m venv .venv
|
|
44
|
+
# On Windows:
|
|
45
|
+
.\.venv\Scripts\activate
|
|
46
|
+
# On Linux/macOS:
|
|
47
|
+
source .venv/bin/activate
|
|
48
|
+
|
|
49
|
+
# Install package
|
|
50
|
+
pip install -e .
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
### 3. Check Detected Hardware
|
|
56
|
+
|
|
57
|
+
Verify that your GPU and local models are recognized:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
nexus-worker info
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Example output:
|
|
64
|
+
```text
|
|
65
|
+
============================================================
|
|
66
|
+
NEXUSCOMPUTE HARDWARE DETECTOR
|
|
67
|
+
============================================================
|
|
68
|
+
• GPU: NVIDIA GeForce RTX 4090
|
|
69
|
+
• VRAM: 24,576 MB
|
|
70
|
+
• Backend Engine: Ollama (http://127.0.0.1:11434)
|
|
71
|
+
• Discovered Models:
|
|
72
|
+
- llama3:8b
|
|
73
|
+
- deepseek-r1:7b
|
|
74
|
+
- mistral:7b
|
|
75
|
+
============================================================
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
### 4. Connect to Network & Earn
|
|
81
|
+
|
|
82
|
+
Get your node token from the **Provider Dashboard** on NexusCompute, then start the daemon:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
nexus-worker start --token nx-node-YOUR_TOKEN_HERE
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
#### CLI Options:
|
|
89
|
+
* `--token TEXT`: Your unique node authorization token *(required)*.
|
|
90
|
+
* `--coordinator TEXT`: Coordinator WebSocket URL (default: `ws://127.0.0.1:8000/api/v1/ws/worker`).
|
|
91
|
+
* `--engine [auto|ollama|vllm|emulate]`: Local inference engine (default: `auto`).
|
|
92
|
+
* `--emulate`: Run in emulation mode for development without downloading weights.
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# Example with custom coordinator and auto-discovery
|
|
96
|
+
nexus-worker start --token nx-node-0a0a3b29ed4e323af354549a6681821a1b75157e
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## 🛠️ Supported Engines
|
|
102
|
+
|
|
103
|
+
1. **Ollama (Recommended)**: Pull models via `ollama pull llama3:8b`. NexusWorker will auto-detect active models and advertise them to the coordinator.
|
|
104
|
+
2. **vLLM (High-Throughput)**: Launch with OpenAI-compatible API (`--port 8000`).
|
|
105
|
+
3. **Emulation Engine**: Built-in mock tokenizer and streaming generator for rapid testing.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## 📄 License
|
|
110
|
+
|
|
111
|
+
Distributed under the **MIT License**. See [LICENSE](LICENSE) for more information.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import os
|
|
3
|
+
import asyncio
|
|
4
|
+
import click
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
from rich.panel import Panel
|
|
7
|
+
from rich.table import Table
|
|
8
|
+
|
|
9
|
+
# Add package root to sys.path
|
|
10
|
+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
|
11
|
+
|
|
12
|
+
from nexus_worker.hardware import detect_hardware
|
|
13
|
+
from nexus_worker.engine import InferenceEngineClient
|
|
14
|
+
from nexus_worker.client import WorkerDaemon
|
|
15
|
+
|
|
16
|
+
console = Console()
|
|
17
|
+
|
|
18
|
+
BANNER = r"""
|
|
19
|
+
[bold cyan] _ _ ____ _ [/bold cyan]
|
|
20
|
+
[bold cyan] | \ | | _____ ___ _ ___ / ___|___ _ __ ___ _ __ _ _| |_ ___ [/bold cyan]
|
|
21
|
+
[bold cyan] | \| |/ _ \ \/ / | | / __| | / _ \| '_ ` _ \| '_ \| | | | __/ _ \[/bold cyan]
|
|
22
|
+
[bold cyan] | |\ | __/> <| |_| \__ \ |__| (_) | | | | | | |_) | |_| | || __/[/bold cyan]
|
|
23
|
+
[bold cyan] |_| \_|\___/_/\_\\__,_|___/\____\___/|_| |_| |_| .__/ \__,_|\__\___|[/bold cyan]
|
|
24
|
+
[bold cyan] |_| [/bold cyan]
|
|
25
|
+
[bold yellow] >>> Decentralized AI Inference Provider Node (Antseed-Compatible) <<< [/bold yellow]
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
@click.group()
|
|
29
|
+
def cli():
|
|
30
|
+
"""NexusCompute Worker CLI - Monetize your GPU by serving open-source AI inference."""
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
@cli.command()
|
|
34
|
+
def info():
|
|
35
|
+
"""Display local GPU hardware specifications and discovered AI engines."""
|
|
36
|
+
console.print(BANNER)
|
|
37
|
+
hw = detect_hardware()
|
|
38
|
+
|
|
39
|
+
table = Table(title="Detected Hardware & Compute Environment", style="cyan")
|
|
40
|
+
table.add_column("Property", style="bold white")
|
|
41
|
+
table.add_column("Detected Value", style="green")
|
|
42
|
+
|
|
43
|
+
table.add_row("GPU Model", hw["gpu_name"])
|
|
44
|
+
table.add_row("VRAM Capacity", f"{hw['vram_mb']} MB")
|
|
45
|
+
table.add_row("Platform Backend", hw["platform"])
|
|
46
|
+
|
|
47
|
+
console.print(table)
|
|
48
|
+
|
|
49
|
+
# Check local engines
|
|
50
|
+
console.print("\n[bold yellow]Scanning for Local AI Inference Engines...[/bold yellow]")
|
|
51
|
+
engine = InferenceEngineClient()
|
|
52
|
+
models = engine.discover_models()
|
|
53
|
+
|
|
54
|
+
engine_table = Table(title="Inference Engine Status", style="magenta")
|
|
55
|
+
engine_table.add_column("Engine", style="bold white")
|
|
56
|
+
engine_table.add_column("Available Models", style="green")
|
|
57
|
+
engine_table.add_row(engine.active_engine.upper(), ", ".join(models) if models else "None detected")
|
|
58
|
+
console.print(engine_table)
|
|
59
|
+
|
|
60
|
+
@cli.command()
|
|
61
|
+
@click.option("--token", "-t", required=True, help="Provider node authentication token (nx-node-...) from dashboard.")
|
|
62
|
+
@click.option("--coordinator", "-c", "--server", "-s", default="https://nexus-compute.pages.dev", help="NexusCompute Coordinator URL (e.g. https://nexus-compute.pages.dev).")
|
|
63
|
+
@click.option("--ollama-url", default="http://localhost:11434", help="Local Ollama HTTP endpoint.")
|
|
64
|
+
@click.option("--vllm-url", default="http://localhost:8000", help="Local vLLM HTTP endpoint.")
|
|
65
|
+
@click.option("--emulate", is_flag=True, default=False, help="Enable test emulation mode (simulates GPU streaming without Ollama).")
|
|
66
|
+
@click.option("--prompt-price", default=0.20, type=float, help="Custom price per 1M prompt tokens (in credits).")
|
|
67
|
+
@click.option("--completion-price", default=0.40, type=float, help="Custom price per 1M completion tokens (in credits).")
|
|
68
|
+
def start(token, coordinator, ollama_url, vllm_url, emulate, prompt_price, completion_price):
|
|
69
|
+
"""Start the provider daemon and connect outbound to the NexusCompute network."""
|
|
70
|
+
console.print(BANNER)
|
|
71
|
+
console.print(Panel(
|
|
72
|
+
f"[bold green]Node Token:[/bold green] {token[:12]}...\n"
|
|
73
|
+
f"[bold green]Coordinator Target:[/bold green] {coordinator}\n"
|
|
74
|
+
f"[bold green]Pricing Config:[/bold green] ${prompt_price}/M prompt | ${completion_price}/M completion\n"
|
|
75
|
+
f"[bold green]Emulation Mode:[/bold green] {'Active' if emulate else 'Auto-detect'}",
|
|
76
|
+
title="Worker Configuration",
|
|
77
|
+
style="blue"
|
|
78
|
+
))
|
|
79
|
+
|
|
80
|
+
daemon = WorkerDaemon(
|
|
81
|
+
node_token=token,
|
|
82
|
+
coordinator_url=coordinator,
|
|
83
|
+
ollama_url=ollama_url,
|
|
84
|
+
vllm_url=vllm_url,
|
|
85
|
+
emulate=emulate,
|
|
86
|
+
prompt_price=prompt_price,
|
|
87
|
+
completion_price=completion_price
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
asyncio.run(daemon.start())
|
|
92
|
+
except KeyboardInterrupt:
|
|
93
|
+
console.print("\n[bold red]Worker shut down gracefully by operator.[/bold red]")
|
|
94
|
+
|
|
95
|
+
if __name__ == "__main__":
|
|
96
|
+
cli()
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
import time
|
|
5
|
+
import urllib.parse
|
|
6
|
+
from typing import Dict, Any, List, Optional
|
|
7
|
+
import httpx
|
|
8
|
+
|
|
9
|
+
from nexus_worker.hardware import detect_hardware
|
|
10
|
+
from nexus_worker.engine import InferenceEngineClient
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger("nexus_worker")
|
|
13
|
+
|
|
14
|
+
class WorkerDaemon:
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
node_token: str,
|
|
18
|
+
coordinator_url: str = "https://nexus-compute.pages.dev",
|
|
19
|
+
ollama_url: str = "http://localhost:11434",
|
|
20
|
+
vllm_url: str = "http://localhost:8000",
|
|
21
|
+
emulate: bool = False,
|
|
22
|
+
prompt_price: float = 0.20,
|
|
23
|
+
completion_price: float = 0.40
|
|
24
|
+
):
|
|
25
|
+
self.node_token = node_token
|
|
26
|
+
# Normalize coordinator URL
|
|
27
|
+
url = coordinator_url.rstrip("/")
|
|
28
|
+
if url.startswith("ws://"):
|
|
29
|
+
url = url.replace("ws://", "http://", 1)
|
|
30
|
+
elif url.startswith("wss://"):
|
|
31
|
+
url = url.replace("wss://", "https://", 1)
|
|
32
|
+
# Remove trailing path if old /api/v1/ws/worker path was passed
|
|
33
|
+
if "/api/v1/ws/worker" in url:
|
|
34
|
+
url = url.replace("/api/v1/ws/worker", "")
|
|
35
|
+
self.coordinator_url = url
|
|
36
|
+
|
|
37
|
+
self.prompt_price = prompt_price
|
|
38
|
+
self.completion_price = completion_price
|
|
39
|
+
self.engine = InferenceEngineClient(ollama_url=ollama_url, vllm_url=vllm_url, emulate=emulate)
|
|
40
|
+
self.running = False
|
|
41
|
+
|
|
42
|
+
async def start(self):
|
|
43
|
+
self.running = True
|
|
44
|
+
|
|
45
|
+
# 1. Detect hardware
|
|
46
|
+
hw = detect_hardware()
|
|
47
|
+
print(f"\n[NexusCompute Worker] Detected Hardware: {hw['gpu_name']} ({hw['vram_mb']} MB VRAM)")
|
|
48
|
+
|
|
49
|
+
# 2. Discover local models
|
|
50
|
+
models = self.engine.discover_models()
|
|
51
|
+
print(f"[NexusCompute Worker] Engine: {self.engine.active_engine.upper()} | Serving {len(models)} model(s): {', '.join(models)}")
|
|
52
|
+
|
|
53
|
+
# 3. Benchmark tokens/sec
|
|
54
|
+
print("[NexusCompute Worker] Running startup synthetic benchmark probe...")
|
|
55
|
+
bench_speed = await self.engine.run_benchmark()
|
|
56
|
+
print(f"[NexusCompute Worker] Benchmark Result: {bench_speed} tokens/second")
|
|
57
|
+
|
|
58
|
+
models_param = ",".join(models) if models else "llama3:8b,deepseek-r1:7b,mistral:7b"
|
|
59
|
+
headers = {
|
|
60
|
+
"Authorization": f"Bearer {self.node_token}",
|
|
61
|
+
"User-Agent": "NexusWorker/1.0",
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
print(f"[NexusCompute Worker] Connecting to Cloudflare Edge Coordinator: {self.coordinator_url}")
|
|
65
|
+
print("[NexusCompute Worker] Node is ONLINE and actively polling for AI inference jobs...\n")
|
|
66
|
+
|
|
67
|
+
backoff = 2
|
|
68
|
+
async with httpx.AsyncClient(
|
|
69
|
+
headers=headers,
|
|
70
|
+
timeout=httpx.Timeout(connect=10.0, read=35.0, write=10.0, pool=10.0)
|
|
71
|
+
) as client:
|
|
72
|
+
while self.running:
|
|
73
|
+
try:
|
|
74
|
+
poll_url = (
|
|
75
|
+
f"{self.coordinator_url}/api/v1/jobs/poll"
|
|
76
|
+
f"?models={urllib.parse.quote(models_param)}"
|
|
77
|
+
f"&gpu_name={urllib.parse.quote(hw['gpu_name'])}"
|
|
78
|
+
f"&vram_mb={hw['vram_mb']}"
|
|
79
|
+
f"&tokens_per_sec={bench_speed}"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
resp = await client.get(poll_url)
|
|
83
|
+
if resp.status_code == 200:
|
|
84
|
+
backoff = 2 # Reset on successful network request
|
|
85
|
+
data = resp.json()
|
|
86
|
+
job_id = data.get("job_id")
|
|
87
|
+
|
|
88
|
+
if job_id:
|
|
89
|
+
# Job acquired! Process and stream tokens
|
|
90
|
+
await self._process_job(client, data)
|
|
91
|
+
else:
|
|
92
|
+
# Timeout / heartbeat ack, continue immediately
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
elif resp.status_code == 401:
|
|
96
|
+
print(f"[NexusCompute Worker] [ERROR] Authentication failed! Invalid node token: {self.node_token[:12]}...")
|
|
97
|
+
await asyncio.sleep(10)
|
|
98
|
+
else:
|
|
99
|
+
print(f"[NexusCompute Worker] Polling returned status {resp.status_code}. Retrying...")
|
|
100
|
+
await asyncio.sleep(backoff)
|
|
101
|
+
backoff = min(20, backoff * 1.5)
|
|
102
|
+
|
|
103
|
+
except (httpx.ReadTimeout, httpx.ConnectTimeout):
|
|
104
|
+
# Standard long-poll expiration, loop immediately
|
|
105
|
+
continue
|
|
106
|
+
except httpx.RequestError as e:
|
|
107
|
+
print(f"[NexusCompute Worker] Connection error ({type(e).__name__}). Reconnecting in {backoff}s...")
|
|
108
|
+
await asyncio.sleep(backoff)
|
|
109
|
+
backoff = min(20, backoff * 1.5)
|
|
110
|
+
except Exception as e:
|
|
111
|
+
print(f"[NexusCompute Worker] Unexpected error: {e}")
|
|
112
|
+
await asyncio.sleep(backoff)
|
|
113
|
+
|
|
114
|
+
async def _process_job(self, client: httpx.AsyncClient, job_data: dict):
|
|
115
|
+
job_id = job_data.get("job_id")
|
|
116
|
+
model = job_data.get("model")
|
|
117
|
+
messages = job_data.get("messages", [])
|
|
118
|
+
temperature = job_data.get("temperature", 0.7)
|
|
119
|
+
max_tokens = job_data.get("max_tokens", 512)
|
|
120
|
+
|
|
121
|
+
print(f"\n[NexusCompute Worker] >> [CLAIMED] Job {job_id[:8]} for model '{model}'")
|
|
122
|
+
start_time = time.time()
|
|
123
|
+
seq = 0
|
|
124
|
+
prompt_tokens = 15
|
|
125
|
+
completion_tokens = 0
|
|
126
|
+
|
|
127
|
+
try:
|
|
128
|
+
async for chunk in self.engine.stream_chat(
|
|
129
|
+
model=model,
|
|
130
|
+
messages=messages,
|
|
131
|
+
temperature=temperature,
|
|
132
|
+
max_tokens=max_tokens
|
|
133
|
+
):
|
|
134
|
+
if chunk["type"] == "chunk":
|
|
135
|
+
seq += 1
|
|
136
|
+
completion_tokens += 1
|
|
137
|
+
delta = chunk["delta"]
|
|
138
|
+
|
|
139
|
+
# POST chunk to Cloudflare Edge
|
|
140
|
+
stream_url = f"{self.coordinator_url}/api/v1/jobs/{job_id}/stream"
|
|
141
|
+
try:
|
|
142
|
+
await client.post(stream_url, json={"seq": seq, "delta": delta})
|
|
143
|
+
except Exception as ex:
|
|
144
|
+
logger.warning(f"Failed to post chunk {seq}: {ex}")
|
|
145
|
+
|
|
146
|
+
elif chunk["type"] == "done":
|
|
147
|
+
prompt_tokens = chunk.get("prompt_tokens", 15)
|
|
148
|
+
completion_tokens = chunk.get("completion_tokens", completion_tokens)
|
|
149
|
+
break
|
|
150
|
+
|
|
151
|
+
latency_ms = int((time.time() - start_time) * 1000)
|
|
152
|
+
# Settle completion
|
|
153
|
+
complete_url = f"{self.coordinator_url}/api/v1/jobs/{job_id}/complete"
|
|
154
|
+
await client.post(complete_url, json={
|
|
155
|
+
"prompt_tokens": prompt_tokens,
|
|
156
|
+
"completion_tokens": completion_tokens,
|
|
157
|
+
"latency_ms": latency_ms,
|
|
158
|
+
})
|
|
159
|
+
print(f"[NexusCompute Worker] << [COMPLETED] Job {job_id[:8]} in {latency_ms}ms ({completion_tokens} tokens streamed, payout recorded)!\n")
|
|
160
|
+
|
|
161
|
+
except Exception as e:
|
|
162
|
+
print(f"[NexusCompute Worker] !! [ERROR] Job {job_id[:8]} failed: {e}")
|
|
163
|
+
try:
|
|
164
|
+
await client.post(f"{self.coordinator_url}/api/v1/jobs/{job_id}/stream", json={
|
|
165
|
+
"seq": seq + 1,
|
|
166
|
+
"delta": f"\n[Inference Error: {str(e)}]"
|
|
167
|
+
})
|
|
168
|
+
await client.post(f"{self.coordinator_url}/api/v1/jobs/{job_id}/complete", json={
|
|
169
|
+
"prompt_tokens": prompt_tokens,
|
|
170
|
+
"completion_tokens": completion_tokens,
|
|
171
|
+
"error": str(e)
|
|
172
|
+
})
|
|
173
|
+
except Exception:
|
|
174
|
+
pass
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
import time
|
|
3
|
+
import json
|
|
4
|
+
import asyncio
|
|
5
|
+
from typing import List, Dict, Any, AsyncGenerator
|
|
6
|
+
import httpx
|
|
7
|
+
|
|
8
|
+
class InferenceEngineClient:
|
|
9
|
+
def __init__(self, ollama_url: str = "http://localhost:11434", vllm_url: str = "http://localhost:8000", emulate: bool = False):
|
|
10
|
+
self.ollama_url = ollama_url.rstrip("/")
|
|
11
|
+
self.vllm_url = vllm_url.rstrip("/")
|
|
12
|
+
self.emulate = emulate
|
|
13
|
+
self.active_engine = "none"
|
|
14
|
+
|
|
15
|
+
def discover_models(self) -> List[str]:
|
|
16
|
+
"""Discovers locally installed models across Ollama, vLLM, or emulation."""
|
|
17
|
+
if self.emulate:
|
|
18
|
+
self.active_engine = "emulated"
|
|
19
|
+
return ["llama3:8b", "mistral:7b", "deepseek-r1:7b"]
|
|
20
|
+
|
|
21
|
+
# 1. Check if Ollama executable exists on system
|
|
22
|
+
if shutil.which("ollama"):
|
|
23
|
+
try:
|
|
24
|
+
import urllib.request
|
|
25
|
+
req = urllib.request.Request(f"{self.ollama_url}/api/tags", headers={"User-Agent": "NexusWorker"})
|
|
26
|
+
with urllib.request.urlopen(req, timeout=1.0) as response:
|
|
27
|
+
if response.status == 200:
|
|
28
|
+
data = json.loads(response.read().decode())
|
|
29
|
+
models = [m["name"] for m in data.get("models", [])]
|
|
30
|
+
if models:
|
|
31
|
+
self.active_engine = "ollama"
|
|
32
|
+
return models
|
|
33
|
+
except Exception:
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
# 2. Fallback to Emulated Engine for testing/demo
|
|
37
|
+
self.active_engine = "emulated"
|
|
38
|
+
return ["llama3:8b", "mistral:7b", "deepseek-r1:7b"]
|
|
39
|
+
|
|
40
|
+
async def stream_chat(
|
|
41
|
+
self,
|
|
42
|
+
model: str,
|
|
43
|
+
messages: List[Dict[str, str]],
|
|
44
|
+
temperature: float = 0.7,
|
|
45
|
+
max_tokens: int = 1024
|
|
46
|
+
) -> AsyncGenerator[Dict[str, Any], None]:
|
|
47
|
+
"""Streams tokens from local engine or emulated runtime."""
|
|
48
|
+
if self.active_engine == "ollama":
|
|
49
|
+
async with httpx.AsyncClient(timeout=60.0) as client:
|
|
50
|
+
async with client.stream(
|
|
51
|
+
"POST",
|
|
52
|
+
f"{self.ollama_url}/api/chat",
|
|
53
|
+
json={
|
|
54
|
+
"model": model,
|
|
55
|
+
"messages": messages,
|
|
56
|
+
"options": {"temperature": temperature, "num_predict": max_tokens},
|
|
57
|
+
"stream": True
|
|
58
|
+
}
|
|
59
|
+
) as response:
|
|
60
|
+
async for line in response.aiter_lines():
|
|
61
|
+
if line:
|
|
62
|
+
data = json.loads(line)
|
|
63
|
+
chunk_text = data.get("message", {}).get("content", "")
|
|
64
|
+
is_done = data.get("done", False)
|
|
65
|
+
prompt_eval = data.get("prompt_eval_count", 0)
|
|
66
|
+
eval_count = data.get("eval_count", 0)
|
|
67
|
+
|
|
68
|
+
if chunk_text:
|
|
69
|
+
yield {"type": "chunk", "delta": chunk_text}
|
|
70
|
+
if is_done:
|
|
71
|
+
yield {
|
|
72
|
+
"type": "done",
|
|
73
|
+
"prompt_tokens": prompt_eval or 15,
|
|
74
|
+
"completion_tokens": eval_count or 40,
|
|
75
|
+
"finish_reason": "stop"
|
|
76
|
+
}
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
elif self.active_engine == "vllm":
|
|
80
|
+
async with httpx.AsyncClient(timeout=60.0) as client:
|
|
81
|
+
async with client.stream(
|
|
82
|
+
"POST",
|
|
83
|
+
f"{self.vllm_url}/v1/chat/completions",
|
|
84
|
+
json={
|
|
85
|
+
"model": model,
|
|
86
|
+
"messages": messages,
|
|
87
|
+
"temperature": temperature,
|
|
88
|
+
"max_tokens": max_tokens,
|
|
89
|
+
"stream": True
|
|
90
|
+
}
|
|
91
|
+
) as response:
|
|
92
|
+
async for line in response.aiter_lines():
|
|
93
|
+
if line.startswith("data: "):
|
|
94
|
+
body = line[6:].strip()
|
|
95
|
+
if body == "[DONE]":
|
|
96
|
+
yield {"type": "done", "prompt_tokens": 15, "completion_tokens": 40, "finish_reason": "stop"}
|
|
97
|
+
return
|
|
98
|
+
data = json.loads(body)
|
|
99
|
+
delta = data["choices"][0]["delta"].get("content", "")
|
|
100
|
+
if delta:
|
|
101
|
+
yield {"type": "chunk", "delta": delta}
|
|
102
|
+
|
|
103
|
+
else:
|
|
104
|
+
# Emulated engine: streams realistic AI response with realistic latency
|
|
105
|
+
user_prompt = messages[-1]["content"] if messages else "Hello"
|
|
106
|
+
|
|
107
|
+
# Generate thoughtful response text
|
|
108
|
+
simulated_response = (
|
|
109
|
+
f"[NexusCompute Verified Node] Processed request for '{model}'.\n\n"
|
|
110
|
+
f"Here is the generated inference for: \"{user_prompt}\"\n\n"
|
|
111
|
+
"Decentralized AI inference transforms commodity GPUs into an open, permissionless compute fabric. "
|
|
112
|
+
"By decoupling model execution from centralized cloud monopolies, providers earn competitive yields while "
|
|
113
|
+
"developers enjoy low latency, verifiable hardware proofs, and lower token costs."
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
words = simulated_response.split(" ")
|
|
117
|
+
for i, word in enumerate(words):
|
|
118
|
+
chunk = word + (" " if i < len(words) - 1 else "")
|
|
119
|
+
yield {"type": "chunk", "delta": chunk}
|
|
120
|
+
await asyncio.sleep(0.04) # ~25-30 tokens/sec simulated speed
|
|
121
|
+
|
|
122
|
+
yield {
|
|
123
|
+
"type": "done",
|
|
124
|
+
"prompt_tokens": max(5, len(user_prompt.split())),
|
|
125
|
+
"completion_tokens": len(words),
|
|
126
|
+
"finish_reason": "stop"
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async def run_benchmark(self) -> float:
|
|
130
|
+
"""Measures real tokens/sec on startup."""
|
|
131
|
+
start = time.time()
|
|
132
|
+
tokens_emitted = 0
|
|
133
|
+
async for chunk in self.stream_chat(
|
|
134
|
+
model="llama3:8b",
|
|
135
|
+
messages=[{"role": "user", "content": "Benchmark speed test."}],
|
|
136
|
+
max_tokens=30
|
|
137
|
+
):
|
|
138
|
+
if chunk["type"] == "chunk":
|
|
139
|
+
tokens_emitted += 1
|
|
140
|
+
elif chunk["type"] == "done":
|
|
141
|
+
tokens_emitted = chunk.get("completion_tokens", tokens_emitted)
|
|
142
|
+
break
|
|
143
|
+
|
|
144
|
+
elapsed = max(0.1, time.time() - start)
|
|
145
|
+
tok_sec = round(tokens_emitted / elapsed, 1)
|
|
146
|
+
return max(15.0, tok_sec)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import platform
|
|
3
|
+
import shutil
|
|
4
|
+
from typing import Dict, Any
|
|
5
|
+
|
|
6
|
+
def detect_hardware() -> Dict[str, Any]:
|
|
7
|
+
"""Detects local GPU and memory resources with cross-platform fallback."""
|
|
8
|
+
# 1. Check NVIDIA GPU via nvidia-smi
|
|
9
|
+
nvidia_smi = shutil.which("nvidia-smi")
|
|
10
|
+
if nvidia_smi:
|
|
11
|
+
try:
|
|
12
|
+
# Query GPU name and total memory in MB
|
|
13
|
+
result = subprocess.run(
|
|
14
|
+
["nvidia-smi", "--query-gpu=gpu_name,memory.total", "--format=csv,noheader,nounits"],
|
|
15
|
+
capture_output=True,
|
|
16
|
+
text=True,
|
|
17
|
+
check=True
|
|
18
|
+
)
|
|
19
|
+
line = result.stdout.strip().split("\n")[0]
|
|
20
|
+
parts = line.split(",")
|
|
21
|
+
if len(parts) == 2:
|
|
22
|
+
gpu_name = parts[0].strip()
|
|
23
|
+
vram_mb = int(parts[1].strip())
|
|
24
|
+
return {
|
|
25
|
+
"gpu_name": gpu_name,
|
|
26
|
+
"vram_mb": vram_mb,
|
|
27
|
+
"platform": "nvidia_cuda"
|
|
28
|
+
}
|
|
29
|
+
except Exception:
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
# 2. Check Apple Silicon (macOS)
|
|
33
|
+
if platform.system() == "Darwin" and platform.machine() == "arm64":
|
|
34
|
+
try:
|
|
35
|
+
sysctl_res = subprocess.run(["sysctl", "-n", "machdep.cpu.brand_string"], capture_output=True, text=True)
|
|
36
|
+
chip_name = sysctl_res.stdout.strip() or "Apple Silicon"
|
|
37
|
+
# Get total RAM
|
|
38
|
+
mem_res = subprocess.run(["sysctl", "-n", "hw.memsize"], capture_output=True, text=True)
|
|
39
|
+
total_bytes = int(mem_res.stdout.strip())
|
|
40
|
+
vram_mb = int(total_bytes / (1024 * 1024))
|
|
41
|
+
return {
|
|
42
|
+
"gpu_name": f"{chip_name} (Unified Memory)",
|
|
43
|
+
"vram_mb": vram_mb,
|
|
44
|
+
"platform": "apple_metal"
|
|
45
|
+
}
|
|
46
|
+
except Exception:
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
# 3. CPU Fallback
|
|
50
|
+
import os
|
|
51
|
+
cpu_count = os.cpu_count() or 4
|
|
52
|
+
return {
|
|
53
|
+
"gpu_name": f"CPU Node ({cpu_count} cores, {platform.machine()})",
|
|
54
|
+
"vram_mb": 8192,
|
|
55
|
+
"platform": "cpu"
|
|
56
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nexus-worker
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Monetize your GPU by serving open-source AI inference in the NexusCompute decentralized network.
|
|
5
|
+
Author-email: NexusCompute Network <providers@nexuscompute.ai>
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
11
|
+
Requires-Python: >=3.9
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: httpx>=0.27.0
|
|
15
|
+
Requires-Dist: websockets>=12.0
|
|
16
|
+
Requires-Dist: click>=8.1.7
|
|
17
|
+
Requires-Dist: rich>=13.7.0
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# ⚡ NexusWorker (`nexus-worker`)
|
|
21
|
+
|
|
22
|
+
[](LICENSE)
|
|
23
|
+
[](https://www.python.org/downloads/)
|
|
24
|
+
[](https://nexuscompute.ai)
|
|
25
|
+
|
|
26
|
+
**NexusWorker** is the open-source provider daemon for the **NexusCompute** decentralized AI inference network.
|
|
27
|
+
|
|
28
|
+
Run this lightweight daemon on your PC, mining rig, or server to monetize your idle graphics cards (NVIDIA, AMD, Apple Silicon) by serving inference tokens for open-source LLMs (Llama 3, DeepSeek-R1, Mistral).
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 🔒 Security & Privacy (Outbound-Only)
|
|
33
|
+
|
|
34
|
+
NexusWorker was designed from the ground up for strict security:
|
|
35
|
+
|
|
36
|
+
* **Zero Inbound Ports**: The daemon initiates an **outbound-only** encrypted WebSocket connection to the NexusCompute coordinator. You **never** need to configure port forwarding, open firewall rules, or expose a public IP.
|
|
37
|
+
* **Completely Sandboxed**: The worker only communicates with your local inference engine (e.g. Ollama or vLLM) over localhost (`127.0.0.1`).
|
|
38
|
+
* **100% Open Source**: Full transparency—inspect every line of code before running.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 🚀 Quickstart
|
|
43
|
+
|
|
44
|
+
### 1. Requirements
|
|
45
|
+
|
|
46
|
+
* **OS**: Linux, Windows, or macOS
|
|
47
|
+
* **Python**: 3.9 or higher
|
|
48
|
+
* **Inference Engine**: [Ollama](https://ollama.ai) (recommended for 1-click setup) or [vLLM](https://github.com/vllm-project/vllm).
|
|
49
|
+
* **Hardware**: Any modern GPU with 6GB+ VRAM (e.g. RTX 3060/3080/4090, Apple M-series), or CPU in emulation mode.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
### 2. Installation
|
|
54
|
+
|
|
55
|
+
Clone and install in a Python virtual environment:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
git clone https://github.com/nexus-compute-ai/nexus-worker.git
|
|
59
|
+
cd nexus-worker
|
|
60
|
+
|
|
61
|
+
# Create and activate virtual environment
|
|
62
|
+
python -m venv .venv
|
|
63
|
+
# On Windows:
|
|
64
|
+
.\.venv\Scripts\activate
|
|
65
|
+
# On Linux/macOS:
|
|
66
|
+
source .venv/bin/activate
|
|
67
|
+
|
|
68
|
+
# Install package
|
|
69
|
+
pip install -e .
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
### 3. Check Detected Hardware
|
|
75
|
+
|
|
76
|
+
Verify that your GPU and local models are recognized:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
nexus-worker info
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Example output:
|
|
83
|
+
```text
|
|
84
|
+
============================================================
|
|
85
|
+
NEXUSCOMPUTE HARDWARE DETECTOR
|
|
86
|
+
============================================================
|
|
87
|
+
• GPU: NVIDIA GeForce RTX 4090
|
|
88
|
+
• VRAM: 24,576 MB
|
|
89
|
+
• Backend Engine: Ollama (http://127.0.0.1:11434)
|
|
90
|
+
• Discovered Models:
|
|
91
|
+
- llama3:8b
|
|
92
|
+
- deepseek-r1:7b
|
|
93
|
+
- mistral:7b
|
|
94
|
+
============================================================
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
### 4. Connect to Network & Earn
|
|
100
|
+
|
|
101
|
+
Get your node token from the **Provider Dashboard** on NexusCompute, then start the daemon:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
nexus-worker start --token nx-node-YOUR_TOKEN_HERE
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
#### CLI Options:
|
|
108
|
+
* `--token TEXT`: Your unique node authorization token *(required)*.
|
|
109
|
+
* `--coordinator TEXT`: Coordinator WebSocket URL (default: `ws://127.0.0.1:8000/api/v1/ws/worker`).
|
|
110
|
+
* `--engine [auto|ollama|vllm|emulate]`: Local inference engine (default: `auto`).
|
|
111
|
+
* `--emulate`: Run in emulation mode for development without downloading weights.
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# Example with custom coordinator and auto-discovery
|
|
115
|
+
nexus-worker start --token nx-node-0a0a3b29ed4e323af354549a6681821a1b75157e
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## 🛠️ Supported Engines
|
|
121
|
+
|
|
122
|
+
1. **Ollama (Recommended)**: Pull models via `ollama pull llama3:8b`. NexusWorker will auto-detect active models and advertise them to the coordinator.
|
|
123
|
+
2. **vLLM (High-Throughput)**: Launch with OpenAI-compatible API (`--port 8000`).
|
|
124
|
+
3. **Emulation Engine**: Built-in mock tokenizer and streaming generator for rapid testing.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 📄 License
|
|
129
|
+
|
|
130
|
+
Distributed under the **MIT License**. See [LICENSE](LICENSE) for more information.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
setup.py
|
|
5
|
+
nexus_worker/__init__.py
|
|
6
|
+
nexus_worker/cli.py
|
|
7
|
+
nexus_worker/client.py
|
|
8
|
+
nexus_worker/engine.py
|
|
9
|
+
nexus_worker/hardware.py
|
|
10
|
+
nexus_worker.egg-info/PKG-INFO
|
|
11
|
+
nexus_worker.egg-info/SOURCES.txt
|
|
12
|
+
nexus_worker.egg-info/dependency_links.txt
|
|
13
|
+
nexus_worker.egg-info/entry_points.txt
|
|
14
|
+
nexus_worker.egg-info/requires.txt
|
|
15
|
+
nexus_worker.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
nexus_worker
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "nexus-worker"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Monetize your GPU by serving open-source AI inference in the NexusCompute decentralized network."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
authors = [{ name = "NexusCompute Network", email = "providers@nexuscompute.ai" }]
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
"License :: OSI Approved :: MIT License",
|
|
15
|
+
"Operating System :: OS Independent",
|
|
16
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
17
|
+
]
|
|
18
|
+
dependencies = [
|
|
19
|
+
"httpx>=0.27.0",
|
|
20
|
+
"websockets>=12.0",
|
|
21
|
+
"click>=8.1.7",
|
|
22
|
+
"rich>=13.7.0",
|
|
23
|
+
]
|
|
24
|
+
requires-python = ">=3.9"
|
|
25
|
+
|
|
26
|
+
[project.scripts]
|
|
27
|
+
nexus-worker = "nexus_worker.cli:cli"
|
|
28
|
+
|
|
29
|
+
[tool.setuptools.packages.find]
|
|
30
|
+
where = ["."]
|
|
31
|
+
include = ["nexus_worker*"]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name="nexus-worker",
|
|
5
|
+
version="0.1.0",
|
|
6
|
+
packages=find_packages(),
|
|
7
|
+
install_requires=[
|
|
8
|
+
"httpx>=0.27.0",
|
|
9
|
+
"websockets>=12.0",
|
|
10
|
+
"click>=8.1.7",
|
|
11
|
+
"rich>=13.7.0",
|
|
12
|
+
],
|
|
13
|
+
entry_points={
|
|
14
|
+
"console_scripts": [
|
|
15
|
+
"nexus-worker = nexus_worker.cli:cli",
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
)
|