Metadata-Version: 2.4
Name: orvix-node
Version: 0.2.1
Summary: Orvix Node Software — run GPU compute for the Orvix decentralized AI network.
Author: Orvix
License: MIT
Project-URL: Homepage, https://orvix.network
Project-URL: Repository, https://github.com/OrvixCompute/orvix
Project-URL: Issues, https://github.com/OrvixCompute/orvix/issues
Project-URL: Documentation, https://github.com/OrvixCompute/orvix/blob/main/orvix-node/README.md
Keywords: gpu,inference,llm,solana,decentralized,openai-compatible
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: GPU :: NVIDIA CUDA
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: System :: Distributed Computing
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: click>=8.1
Requires-Dist: loguru>=0.7
Requires-Dist: pyyaml>=6.0
Requires-Dist: pydantic>=2.7
Requires-Dist: pydantic-settings>=2.3
Requires-Dist: fastapi>=0.110
Requires-Dist: uvicorn>=0.29
Requires-Dist: websockets>=12.0
Requires-Dist: httpx>=0.27
Requires-Dist: tiktoken>=0.7
Provides-Extra: gpu
Requires-Dist: pynvml>=11.5; extra == "gpu"
Requires-Dist: vllm>=0.5; extra == "gpu"
Provides-Extra: image
Requires-Dist: diffusers>=0.31.0; extra == "image"
Requires-Dist: transformers>=4.44.0; extra == "image"
Requires-Dist: accelerate>=0.34.0; extra == "image"
Requires-Dist: sentencepiece>=0.2.0; extra == "image"
Requires-Dist: protobuf>=4.25; extra == "image"
Requires-Dist: Pillow>=10.0; extra == "image"
Provides-Extra: nvml
Requires-Dist: pynvml>=11.5; extra == "nvml"
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"

# Orvix Node Software

A Python agent that runs on a GPU provider's machine. It connects to the Orvix
Orchestrator over WebSocket, registers its GPU, receives inference jobs, runs
them, and returns results — earning USDC for the provider.

Inference is **mocked by default**, so the entire pipeline runs on any machine.
Real GPU inference (vLLM) is a one-file swap once you have a CUDA GPU (Prompt 7).

## Hardware requirements

- For real inference: NVIDIA GPU, CUDA 11+, 8 GB+ VRAM (Linux).
- For development: anything — use `ORVIX_NODE_STUB_GPU=true` and the mock backend.

## Installation

**One-line (Linux providers):**
```bash
curl -sSL https://raw.githubusercontent.com/OrvixCompute/orvix/main/orvix-node/install.sh | bash
orvix-node join            # paste the credentials from the dashboard
orvix-node start
```

**Manual (development, any OS):**
```bash
cd orvix-node
python -m venv .venv
# Windows: .venv\Scripts\Activate.ps1   |   Unix: source .venv/bin/activate
pip install -e .            # core only (mock backend)
# pip install -e .[nvml]    # + real GPU detection (no vLLM)
# pip install -e .[gpu]     # + vLLM for real inference (Linux/CUDA)
```

Verify:
```bash
orvix-node --version
```

## Configuration

Create the config file:
```bash
orvix-node config init        # writes ~/.orvix/config.yaml
orvix-node config show        # prints resolved config (secrets masked)
```

Precedence: **CLI flags > env vars (`ORVIX_NODE_*`) > config file > defaults.**
Required fields: `provider_id`, `node_secret` (get them from
`POST /v1/provider/register` on the orchestrator).

## Running

```bash
# Development without a GPU (mock everything):
ORVIX_NODE_STUB_GPU=true orvix-node start

# Check the GPU detector:
ORVIX_NODE_STUB_GPU=true orvix-node gpu
ORVIX_NODE_STUB_GPU=true orvix-node gpu --watch

# Run inference locally without the orchestrator:
orvix-node test-inference --prompt "Hello, world"
orvix-node test-inference --prompt "Stream this" --stream

# Live status (queries the local health endpoint):
orvix-node status

# Tail logs:
orvix-node logs --tail 100 --follow
```

The node exposes a local health server (default `:9000`):
- `GET /health` → status, uptime, current jobs, GPU health, orchestrator connection
- `GET /metrics` → counters + live GPU metrics

## Running as a systemd service

The installer can set this up, or do it manually:
```ini
# /etc/systemd/system/orvix-node.service
[Service]
ExecStart=%h/.local/bin/orvix-node start
Restart=always
```
```bash
sudo systemctl enable --now orvix-node
systemctl status orvix-node
```

## Connection flow

```
Node                                  Orchestrator
 │ ── WS connect /v1/node/connect ───────▶ │
 │ ── RegisterMessage ───────────────────▶ │  validate provider + secret
 │ ◀── RegisterAck(accepted, node_id) ──── │
 │                                          │
 │ ── Heartbeat (every 15s) ─────────────▶ │  status, current_jobs, GPU metrics
 │ ◀── JobMessage ──────────────────────── │  dispatched inference request
 │ ── JobResult / JobChunk(stream) ──────▶ │  result correlated to the job
 │ ◀── Ping / Shutdown ─────────────────── │
```

On disconnect the node reconnects with exponential backoff (1→2→4…→60s).
A rejected registration (`accepted=false`) is **not** retried.

## Releasing

Publishing is automated and tokenless — PyPI trusts this repository through
OIDC, so there is no API token to leak.

```bash
# 1. bump orvix_node/version.py (the single source; pyproject reads it)
# 2. merge that
git tag node-v0.2.1
git push origin node-v0.2.1
```

The `node-` prefix matters: this repository also tags its own releases as
`v0.2.0`, and the package has a separate version line. The prefix says which
artefact moved.

The workflow refuses to publish if the tag disagrees with `version.py`. PyPI
never lets a version be re-uploaded, so a mismatch is worth failing on rather
than discovering afterwards.

**Providers install the last *published* version.** A fix merged to `main` does
not reach them until a release is cut — set `ORVIX_NODE_REF` to install from a
git ref if you need one before then.

## Architecture

| File | Responsibility |
| ---- | -------------- |
| `cli.py` | Click commands; wires config → GPU → backend → executor → client |
| `config.py` | Layered config (CLI/env/file/defaults), pydantic-validated |
| `gpu.py` | `GPUDetector` (pynvml) with stub mode |
| `protocol.py` | Wire messages — **kept identical with the orchestrator** |
| `client.py` | WebSocket connection, register, heartbeat, reconnect |
| `executor.py` | Concurrency-limited job execution + metrics |
| `inference/` | `base` interface, `mock` (now), `vllm` (Prompt 7) |
| `health.py` | Local FastAPI health/metrics server |
| `state.py` | Singleton runtime state |

## Local integration with the orchestrator

1. Run the orchestrator on `:8000`.
2. Point the node at it: `ORVIX_NODE_ORCHESTRATOR_URL=ws://localhost:8000`.
3. Start the node (`ORVIX_NODE_STUB_GPU=true orvix-node start`).
4. Send a request via the OpenAI client to the orchestrator — it routes to the node.

## Testing

```bash
pip install -e .[dev]
pytest -q

# Standalone client smoke test against an in-process mock server:
ORVIX_NODE_STUB_GPU=true python test_connection.py
```

## Troubleshooting

- **`No GPU detected`** — `pip install --upgrade "orvix-node[nvml]"`, or set
  `ORVIX_NODE_STUB_GPU=true` for development.
- **`Refusing insecure ws://`** — only `ws://localhost` is allowed without TLS;
  use `wss://` for remote orchestrators.
- **Auth failed (exit 2)** — check `provider_id` / `node_secret` against the
  orchestrator's `/v1/provider/register`.

## Roadmap

- Prompt 5–6: orchestrator routes real jobs to nodes; provider earnings/withdrawals.
- Prompt 7: real vLLM inference (replace the mock backend).
