ghostnexus 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.
- ghostnexus-0.1.0/PKG-INFO +161 -0
- ghostnexus-0.1.0/README.md +131 -0
- ghostnexus-0.1.0/ghostnexus/__init__.py +39 -0
- ghostnexus-0.1.0/ghostnexus/client.py +176 -0
- ghostnexus-0.1.0/ghostnexus/exceptions.py +32 -0
- ghostnexus-0.1.0/ghostnexus/models.py +103 -0
- ghostnexus-0.1.0/ghostnexus.egg-info/PKG-INFO +161 -0
- ghostnexus-0.1.0/ghostnexus.egg-info/SOURCES.txt +11 -0
- ghostnexus-0.1.0/ghostnexus.egg-info/dependency_links.txt +1 -0
- ghostnexus-0.1.0/ghostnexus.egg-info/requires.txt +7 -0
- ghostnexus-0.1.0/ghostnexus.egg-info/top_level.txt +1 -0
- ghostnexus-0.1.0/pyproject.toml +39 -0
- ghostnexus-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ghostnexus
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: SDK Python officiel pour GhostNexus — GPU cloud décentralisé, sécurisé & RGPD
|
|
5
|
+
Author-email: GhostNexus <contact@ghostnexus.net>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://ghostnexus.net
|
|
8
|
+
Project-URL: Documentation, https://ghostnexus.net/docs
|
|
9
|
+
Project-URL: Repository, https://github.com/ghostnexus/ghostnexus-python
|
|
10
|
+
Project-URL: Bug Tracker, https://github.com/ghostnexus/ghostnexus-python/issues
|
|
11
|
+
Keywords: gpu,cloud,ai,machine-learning,decentralized,compute
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
Requires-Dist: requests>=2.28
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest; extra == "dev"
|
|
27
|
+
Requires-Dist: responses; extra == "dev"
|
|
28
|
+
Requires-Dist: black; extra == "dev"
|
|
29
|
+
Requires-Dist: mypy; extra == "dev"
|
|
30
|
+
|
|
31
|
+
# GhostNexus Python SDK
|
|
32
|
+
|
|
33
|
+
**Run Python scripts on GDPR-compliant GPU cloud — billed by the second.**
|
|
34
|
+
|
|
35
|
+
[](https://pypi.org/project/ghostnexus/)
|
|
36
|
+
[](https://www.python.org/downloads/)
|
|
37
|
+
[](https://opensource.org/licenses/MIT)
|
|
38
|
+
|
|
39
|
+
GhostNexus is a decentralized GPU compute network. Submit your Python script, our distributed GPUs run it — securely, in the EU, billed by the second.
|
|
40
|
+
|
|
41
|
+
- **RTX 4090** from $0.50/hr · **A100 80GB** from $2.20/hr · **H100** from $3.50/hr
|
|
42
|
+
- GDPR-compliant · EU-hosted · AI Act 2026 ready
|
|
43
|
+
- Docker sandbox isolation · Per-second billing · No minimum deposit
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Install
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install ghostnexus
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Quick Start
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import ghostnexus
|
|
57
|
+
|
|
58
|
+
client = ghostnexus.Client(api_key="gn_live_...")
|
|
59
|
+
|
|
60
|
+
# Submit a script
|
|
61
|
+
job = client.run("train.py")
|
|
62
|
+
|
|
63
|
+
# Wait for result
|
|
64
|
+
result = job.wait()
|
|
65
|
+
print(result.output)
|
|
66
|
+
print(f"Cost: ${result.cost_credits:.4f}")
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Get your API key at [ghostnexus.net/dashboard](https://ghostnexus.net/dashboard) — $15 free credits with code `WELCOME15`.
|
|
70
|
+
|
|
71
|
+
## Examples
|
|
72
|
+
|
|
73
|
+
### Fine-tune a model
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
import ghostnexus
|
|
77
|
+
|
|
78
|
+
client = ghostnexus.Client() # reads GHOSTNEXUS_API_KEY env var
|
|
79
|
+
|
|
80
|
+
job = client.run("finetune_mistral.py", task_name="Mistral-7B LoRA fine-tune")
|
|
81
|
+
result = job.wait(timeout=7200) # 2h max
|
|
82
|
+
|
|
83
|
+
print(result.output)
|
|
84
|
+
# Duration: 1h 23m
|
|
85
|
+
# Cost: $0.69
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Inline script
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
job = client.run(
|
|
92
|
+
"import torch; print(f'CUDA: {torch.cuda.is_available()}')",
|
|
93
|
+
inline=True,
|
|
94
|
+
task_name="cuda-check"
|
|
95
|
+
)
|
|
96
|
+
result = job.wait(timeout=30)
|
|
97
|
+
print(result.output) # CUDA: True
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Check balance
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
balance = client.balance()
|
|
104
|
+
print(f"Available credits: ${balance:.2f}")
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Job history
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
jobs = client.history(limit=10)
|
|
111
|
+
for job in jobs:
|
|
112
|
+
print(f"{job.task_name}: {job.status} — ${job.cost_credits:.4f}")
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Error Handling
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
from ghostnexus.exceptions import InsufficientCreditsError, JobFailedError
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
job = client.run("train.py")
|
|
122
|
+
result = job.wait()
|
|
123
|
+
except InsufficientCreditsError:
|
|
124
|
+
print("Top up at https://ghostnexus.net/dashboard")
|
|
125
|
+
except JobFailedError as e:
|
|
126
|
+
print(f"Job failed. Logs:\n{e.logs}")
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Environment Variable
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
export GHOSTNEXUS_API_KEY="gn_live_..."
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
# No need to pass api_key= explicitly
|
|
137
|
+
client = ghostnexus.Client()
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Pricing
|
|
141
|
+
|
|
142
|
+
| GPU | VRAM | Price | Best for |
|
|
143
|
+
|-----|------|-------|----------|
|
|
144
|
+
| RTX 4070 | 12 GB | $0.25/hr | Inference, small models |
|
|
145
|
+
| RTX 4090 | 24 GB | $0.50/hr | Fine-tuning 7B–13B |
|
|
146
|
+
| RTX A6000 | 48 GB | $0.70/hr | Fine-tuning 30B–70B |
|
|
147
|
+
| A100 80GB | 80 GB | $2.20/hr | Large-scale training |
|
|
148
|
+
| H100 80GB | 80 GB | $3.50/hr | LLM pre-training |
|
|
149
|
+
|
|
150
|
+
Billed **per second** — no hourly rounding, no minimum.
|
|
151
|
+
|
|
152
|
+
## Links
|
|
153
|
+
|
|
154
|
+
- [Dashboard](https://ghostnexus.net/dashboard) — manage jobs and credits
|
|
155
|
+
- [Pricing](https://ghostnexus.net/pricing) — full GPU catalog
|
|
156
|
+
- [Blog](https://ghostnexus.net/blog) — tutorials and guides
|
|
157
|
+
- [Contact](mailto:contact@ghostnexus.net)
|
|
158
|
+
|
|
159
|
+
## License
|
|
160
|
+
|
|
161
|
+
MIT
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# GhostNexus Python SDK
|
|
2
|
+
|
|
3
|
+
**Run Python scripts on GDPR-compliant GPU cloud — billed by the second.**
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/ghostnexus/)
|
|
6
|
+
[](https://www.python.org/downloads/)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
|
|
9
|
+
GhostNexus is a decentralized GPU compute network. Submit your Python script, our distributed GPUs run it — securely, in the EU, billed by the second.
|
|
10
|
+
|
|
11
|
+
- **RTX 4090** from $0.50/hr · **A100 80GB** from $2.20/hr · **H100** from $3.50/hr
|
|
12
|
+
- GDPR-compliant · EU-hosted · AI Act 2026 ready
|
|
13
|
+
- Docker sandbox isolation · Per-second billing · No minimum deposit
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install ghostnexus
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
import ghostnexus
|
|
27
|
+
|
|
28
|
+
client = ghostnexus.Client(api_key="gn_live_...")
|
|
29
|
+
|
|
30
|
+
# Submit a script
|
|
31
|
+
job = client.run("train.py")
|
|
32
|
+
|
|
33
|
+
# Wait for result
|
|
34
|
+
result = job.wait()
|
|
35
|
+
print(result.output)
|
|
36
|
+
print(f"Cost: ${result.cost_credits:.4f}")
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Get your API key at [ghostnexus.net/dashboard](https://ghostnexus.net/dashboard) — $15 free credits with code `WELCOME15`.
|
|
40
|
+
|
|
41
|
+
## Examples
|
|
42
|
+
|
|
43
|
+
### Fine-tune a model
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
import ghostnexus
|
|
47
|
+
|
|
48
|
+
client = ghostnexus.Client() # reads GHOSTNEXUS_API_KEY env var
|
|
49
|
+
|
|
50
|
+
job = client.run("finetune_mistral.py", task_name="Mistral-7B LoRA fine-tune")
|
|
51
|
+
result = job.wait(timeout=7200) # 2h max
|
|
52
|
+
|
|
53
|
+
print(result.output)
|
|
54
|
+
# Duration: 1h 23m
|
|
55
|
+
# Cost: $0.69
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Inline script
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
job = client.run(
|
|
62
|
+
"import torch; print(f'CUDA: {torch.cuda.is_available()}')",
|
|
63
|
+
inline=True,
|
|
64
|
+
task_name="cuda-check"
|
|
65
|
+
)
|
|
66
|
+
result = job.wait(timeout=30)
|
|
67
|
+
print(result.output) # CUDA: True
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Check balance
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
balance = client.balance()
|
|
74
|
+
print(f"Available credits: ${balance:.2f}")
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Job history
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
jobs = client.history(limit=10)
|
|
81
|
+
for job in jobs:
|
|
82
|
+
print(f"{job.task_name}: {job.status} — ${job.cost_credits:.4f}")
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Error Handling
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from ghostnexus.exceptions import InsufficientCreditsError, JobFailedError
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
job = client.run("train.py")
|
|
92
|
+
result = job.wait()
|
|
93
|
+
except InsufficientCreditsError:
|
|
94
|
+
print("Top up at https://ghostnexus.net/dashboard")
|
|
95
|
+
except JobFailedError as e:
|
|
96
|
+
print(f"Job failed. Logs:\n{e.logs}")
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Environment Variable
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
export GHOSTNEXUS_API_KEY="gn_live_..."
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
# No need to pass api_key= explicitly
|
|
107
|
+
client = ghostnexus.Client()
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Pricing
|
|
111
|
+
|
|
112
|
+
| GPU | VRAM | Price | Best for |
|
|
113
|
+
|-----|------|-------|----------|
|
|
114
|
+
| RTX 4070 | 12 GB | $0.25/hr | Inference, small models |
|
|
115
|
+
| RTX 4090 | 24 GB | $0.50/hr | Fine-tuning 7B–13B |
|
|
116
|
+
| RTX A6000 | 48 GB | $0.70/hr | Fine-tuning 30B–70B |
|
|
117
|
+
| A100 80GB | 80 GB | $2.20/hr | Large-scale training |
|
|
118
|
+
| H100 80GB | 80 GB | $3.50/hr | LLM pre-training |
|
|
119
|
+
|
|
120
|
+
Billed **per second** — no hourly rounding, no minimum.
|
|
121
|
+
|
|
122
|
+
## Links
|
|
123
|
+
|
|
124
|
+
- [Dashboard](https://ghostnexus.net/dashboard) — manage jobs and credits
|
|
125
|
+
- [Pricing](https://ghostnexus.net/pricing) — full GPU catalog
|
|
126
|
+
- [Blog](https://ghostnexus.net/blog) — tutorials and guides
|
|
127
|
+
- [Contact](mailto:contact@ghostnexus.net)
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
MIT
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""
|
|
2
|
+
GhostNexus Python SDK
|
|
3
|
+
~~~~~~~~~~~~~~~~~~~~~
|
|
4
|
+
|
|
5
|
+
Run Python scripts on the GhostNexus decentralized GPU network.
|
|
6
|
+
GDPR-compliant, EU-hosted, billed by the second.
|
|
7
|
+
|
|
8
|
+
Quick start:
|
|
9
|
+
import ghostnexus
|
|
10
|
+
client = ghostnexus.Client(api_key="gn_live_...")
|
|
11
|
+
job = client.run("train.py")
|
|
12
|
+
result = job.wait()
|
|
13
|
+
print(result.output)
|
|
14
|
+
|
|
15
|
+
Full documentation: https://ghostnexus.net/docs
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from .client import Client
|
|
19
|
+
from .models import Job, JobResult, UserInfo
|
|
20
|
+
from .exceptions import (
|
|
21
|
+
GhostNexusError,
|
|
22
|
+
AuthenticationError,
|
|
23
|
+
InsufficientCreditsError,
|
|
24
|
+
JobFailedError,
|
|
25
|
+
TimeoutError as GNTimeoutError,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
__version__ = "0.1.0"
|
|
29
|
+
__all__ = [
|
|
30
|
+
"Client",
|
|
31
|
+
"Job",
|
|
32
|
+
"JobResult",
|
|
33
|
+
"UserInfo",
|
|
34
|
+
"GhostNexusError",
|
|
35
|
+
"AuthenticationError",
|
|
36
|
+
"InsufficientCreditsError",
|
|
37
|
+
"JobFailedError",
|
|
38
|
+
"GNTimeoutError",
|
|
39
|
+
]
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""
|
|
2
|
+
GhostNexus Python SDK — Client
|
|
3
|
+
"""
|
|
4
|
+
import os
|
|
5
|
+
import pathlib
|
|
6
|
+
from typing import List, Optional
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
|
|
10
|
+
from .exceptions import (
|
|
11
|
+
AuthenticationError,
|
|
12
|
+
GhostNexusError,
|
|
13
|
+
InsufficientCreditsError,
|
|
14
|
+
ValidationError,
|
|
15
|
+
)
|
|
16
|
+
from .models import Job, JobResult, UserInfo
|
|
17
|
+
|
|
18
|
+
_DEFAULT_BASE_URL = "https://ghostnexus.net"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Client:
|
|
22
|
+
"""
|
|
23
|
+
GhostNexus API client.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
api_key: Your GhostNexus API key (starts with gn_live_).
|
|
27
|
+
If not provided, reads the GHOSTNEXUS_API_KEY environment variable.
|
|
28
|
+
base_url: API base URL (default: https://ghostnexus.net)
|
|
29
|
+
timeout: HTTP timeout in seconds (default: 30)
|
|
30
|
+
|
|
31
|
+
Example:
|
|
32
|
+
>>> import ghostnexus
|
|
33
|
+
>>> client = ghostnexus.Client(api_key="gn_live_...")
|
|
34
|
+
>>> job = client.run("train.py")
|
|
35
|
+
>>> result = job.wait()
|
|
36
|
+
>>> print(result.output)
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
api_key: Optional[str] = None,
|
|
42
|
+
base_url: str = _DEFAULT_BASE_URL,
|
|
43
|
+
timeout: int = 30,
|
|
44
|
+
):
|
|
45
|
+
self._api_key = api_key or os.environ.get("GHOSTNEXUS_API_KEY", "")
|
|
46
|
+
if not self._api_key:
|
|
47
|
+
raise AuthenticationError(
|
|
48
|
+
"Missing API key. Pass api_key= or set the GHOSTNEXUS_API_KEY "
|
|
49
|
+
"environment variable.\n"
|
|
50
|
+
"Get your key at https://ghostnexus.net/dashboard"
|
|
51
|
+
)
|
|
52
|
+
self._base_url = base_url.rstrip("/")
|
|
53
|
+
self._timeout = timeout
|
|
54
|
+
self._session = requests.Session()
|
|
55
|
+
self._session.headers.update({"x-api-key": self._api_key})
|
|
56
|
+
|
|
57
|
+
# ── Account ───────────────────────────────────────────────────────────────
|
|
58
|
+
|
|
59
|
+
def me(self) -> UserInfo:
|
|
60
|
+
"""Return account information (email, credits)."""
|
|
61
|
+
data = self._get("/api/me")
|
|
62
|
+
return UserInfo.from_dict(data)
|
|
63
|
+
|
|
64
|
+
# ── Jobs ──────────────────────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
def run(
|
|
67
|
+
self,
|
|
68
|
+
script: str,
|
|
69
|
+
task_name: Optional[str] = None,
|
|
70
|
+
inline: bool = False,
|
|
71
|
+
) -> Job:
|
|
72
|
+
"""
|
|
73
|
+
Submit a Python script to the GhostNexus GPU network.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
script: Path to a .py file OR inline Python code if inline=True.
|
|
77
|
+
task_name: Job name (shown in dashboard). Defaults to the filename.
|
|
78
|
+
inline: If True, `script` is treated as Python source code directly.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Job — call .wait() to block until completion.
|
|
82
|
+
|
|
83
|
+
Raises:
|
|
84
|
+
AuthenticationError: Invalid API key.
|
|
85
|
+
InsufficientCreditsError: Not enough credits.
|
|
86
|
+
ValidationError: Script rejected by security policy.
|
|
87
|
+
GhostNexusError: Other API error.
|
|
88
|
+
|
|
89
|
+
Example:
|
|
90
|
+
>>> job = client.run("train.py")
|
|
91
|
+
>>> result = job.wait()
|
|
92
|
+
|
|
93
|
+
>>> job = client.run("import torch; print(torch.__version__)", inline=True)
|
|
94
|
+
>>> result = job.wait(timeout=60)
|
|
95
|
+
"""
|
|
96
|
+
if inline:
|
|
97
|
+
script_content = script
|
|
98
|
+
name = task_name or "inline_script"
|
|
99
|
+
else:
|
|
100
|
+
path = pathlib.Path(script)
|
|
101
|
+
if not path.exists():
|
|
102
|
+
raise FileNotFoundError(f"Script not found: {script}")
|
|
103
|
+
script_content = path.read_text(encoding="utf-8")
|
|
104
|
+
name = task_name or path.name
|
|
105
|
+
|
|
106
|
+
data = self._post("/api/jobs", {
|
|
107
|
+
"script_content": script_content,
|
|
108
|
+
"task_name": name,
|
|
109
|
+
})
|
|
110
|
+
return Job.from_dict(data, client=self)
|
|
111
|
+
|
|
112
|
+
def status(self, job_id: str) -> JobResult:
|
|
113
|
+
"""
|
|
114
|
+
Return the current status of a job.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
job_id: Job identifier returned by .run().
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
JobResult with status (pending/dispatched/success/failed), output, cost.
|
|
121
|
+
"""
|
|
122
|
+
data = self._get(f"/api/jobs/{job_id}")
|
|
123
|
+
return JobResult.from_dict(data)
|
|
124
|
+
|
|
125
|
+
def history(self, limit: int = 50, offset: int = 0) -> List[JobResult]:
|
|
126
|
+
"""
|
|
127
|
+
Return job history (paginated).
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
limit: Number of results (max 500).
|
|
131
|
+
offset: Pagination offset.
|
|
132
|
+
"""
|
|
133
|
+
data = self._get("/api/jobs/history", params={"limit": limit, "offset": offset})
|
|
134
|
+
return [JobResult.from_dict(j) for j in data]
|
|
135
|
+
|
|
136
|
+
# ── Credits ───────────────────────────────────────────────────────────────
|
|
137
|
+
|
|
138
|
+
def balance(self) -> float:
|
|
139
|
+
"""Return available credit balance in USD."""
|
|
140
|
+
return self.me().credit_balance
|
|
141
|
+
|
|
142
|
+
# ── Internal HTTP ─────────────────────────────────────────────────────────
|
|
143
|
+
|
|
144
|
+
def _get(self, path: str, params: dict = None) -> dict:
|
|
145
|
+
resp = self._session.get(
|
|
146
|
+
f"{self._base_url}{path}",
|
|
147
|
+
params=params,
|
|
148
|
+
timeout=self._timeout,
|
|
149
|
+
)
|
|
150
|
+
return self._handle(resp)
|
|
151
|
+
|
|
152
|
+
def _post(self, path: str, payload: dict) -> dict:
|
|
153
|
+
resp = self._session.post(
|
|
154
|
+
f"{self._base_url}{path}",
|
|
155
|
+
json=payload,
|
|
156
|
+
timeout=self._timeout,
|
|
157
|
+
)
|
|
158
|
+
return self._handle(resp)
|
|
159
|
+
|
|
160
|
+
@staticmethod
|
|
161
|
+
def _handle(resp: requests.Response) -> dict:
|
|
162
|
+
if resp.status_code == 401:
|
|
163
|
+
raise AuthenticationError("Invalid or expired API key.", status_code=401)
|
|
164
|
+
if resp.status_code == 402:
|
|
165
|
+
detail = resp.json().get("detail", "Insufficient credits.")
|
|
166
|
+
raise InsufficientCreditsError(detail, status_code=402)
|
|
167
|
+
if resp.status_code == 422:
|
|
168
|
+
errors = resp.json().get("detail", [])
|
|
169
|
+
raise ValidationError(f"Script rejected: {errors}", status_code=422)
|
|
170
|
+
if not resp.ok:
|
|
171
|
+
try:
|
|
172
|
+
detail = resp.json().get("detail", resp.text)
|
|
173
|
+
except Exception:
|
|
174
|
+
detail = resp.text
|
|
175
|
+
raise GhostNexusError(detail, status_code=resp.status_code)
|
|
176
|
+
return resp.json()
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""GhostNexus SDK exceptions."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class GhostNexusError(Exception):
|
|
5
|
+
"""Base exception for the GhostNexus SDK."""
|
|
6
|
+
def __init__(self, message: str, status_code: int = None):
|
|
7
|
+
super().__init__(message)
|
|
8
|
+
self.status_code = status_code
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AuthenticationError(GhostNexusError):
|
|
12
|
+
"""Invalid or expired API key."""
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class InsufficientCreditsError(GhostNexusError):
|
|
16
|
+
"""Not enough credits to run the job."""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class JobFailedError(GhostNexusError):
|
|
20
|
+
"""The job failed on the provider side."""
|
|
21
|
+
def __init__(self, message: str, job_id: str = None, logs: str = None):
|
|
22
|
+
super().__init__(message)
|
|
23
|
+
self.job_id = job_id
|
|
24
|
+
self.logs = logs
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class TimeoutError(GhostNexusError):
|
|
28
|
+
"""The job did not complete within the timeout."""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ValidationError(GhostNexusError):
|
|
32
|
+
"""The script was rejected by the security policy."""
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""GhostNexus SDK data models."""
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class UserInfo:
|
|
8
|
+
"""Information about the authenticated user."""
|
|
9
|
+
email: str
|
|
10
|
+
credit_balance: float
|
|
11
|
+
api_key_prefix: str
|
|
12
|
+
is_admin: bool = False
|
|
13
|
+
|
|
14
|
+
@classmethod
|
|
15
|
+
def from_dict(cls, data: dict) -> "UserInfo":
|
|
16
|
+
return cls(
|
|
17
|
+
email=data["email"],
|
|
18
|
+
credit_balance=float(data["credit_balance"]),
|
|
19
|
+
api_key_prefix=data["api_key_prefix"],
|
|
20
|
+
is_admin=data.get("is_admin", False),
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class JobResult:
|
|
26
|
+
"""Result of a completed job."""
|
|
27
|
+
job_id: str
|
|
28
|
+
status: str # success | failed
|
|
29
|
+
output: Optional[str] = None
|
|
30
|
+
duration_seconds: Optional[float] = None
|
|
31
|
+
cost_credits: Optional[float] = None
|
|
32
|
+
task_name: Optional[str] = None
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def success(self) -> bool:
|
|
36
|
+
return self.status == "success"
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def from_dict(cls, data: dict) -> "JobResult":
|
|
40
|
+
return cls(
|
|
41
|
+
job_id=data["job_id"],
|
|
42
|
+
status=data["status"],
|
|
43
|
+
output=data.get("output_logs"),
|
|
44
|
+
duration_seconds=data.get("duration_seconds"),
|
|
45
|
+
cost_credits=float(data["cost_credits"]) if data.get("cost_credits") else None,
|
|
46
|
+
task_name=data.get("task_name"),
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass
|
|
51
|
+
class Job:
|
|
52
|
+
"""
|
|
53
|
+
Represents a job submitted to the GhostNexus network.
|
|
54
|
+
Call .wait() to block until completion, or poll manually with client.status().
|
|
55
|
+
"""
|
|
56
|
+
job_id: str
|
|
57
|
+
task_name: str
|
|
58
|
+
status: str = "pending"
|
|
59
|
+
_client: object = field(default=None, repr=False)
|
|
60
|
+
|
|
61
|
+
def wait(self, timeout: int = 600, poll_interval: int = 3) -> JobResult:
|
|
62
|
+
"""
|
|
63
|
+
Wait for the job to complete (blocking).
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
timeout: Maximum wait time in seconds (default 600s = 10 min).
|
|
67
|
+
poll_interval: Polling interval in seconds.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
JobResult with status, output, cost.
|
|
71
|
+
|
|
72
|
+
Raises:
|
|
73
|
+
TimeoutError: If job does not complete within timeout.
|
|
74
|
+
JobFailedError: If the job failed.
|
|
75
|
+
"""
|
|
76
|
+
import time
|
|
77
|
+
from .exceptions import TimeoutError, JobFailedError
|
|
78
|
+
|
|
79
|
+
start = time.time()
|
|
80
|
+
while True:
|
|
81
|
+
result = self._client.status(self.job_id)
|
|
82
|
+
if result.status in ("success", "failed"):
|
|
83
|
+
if result.status == "failed":
|
|
84
|
+
raise JobFailedError(
|
|
85
|
+
f"Job {self.job_id} failed",
|
|
86
|
+
job_id=self.job_id,
|
|
87
|
+
logs=result.output,
|
|
88
|
+
)
|
|
89
|
+
return result
|
|
90
|
+
if time.time() - start > timeout:
|
|
91
|
+
raise TimeoutError(
|
|
92
|
+
f"Job {self.job_id} not completed after {timeout}s"
|
|
93
|
+
)
|
|
94
|
+
time.sleep(poll_interval)
|
|
95
|
+
|
|
96
|
+
@classmethod
|
|
97
|
+
def from_dict(cls, data: dict, client=None) -> "Job":
|
|
98
|
+
return cls(
|
|
99
|
+
job_id=data["job_id"],
|
|
100
|
+
task_name=data.get("task_name", ""),
|
|
101
|
+
status=data.get("status", "pending"),
|
|
102
|
+
_client=client,
|
|
103
|
+
)
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ghostnexus
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: SDK Python officiel pour GhostNexus — GPU cloud décentralisé, sécurisé & RGPD
|
|
5
|
+
Author-email: GhostNexus <contact@ghostnexus.net>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://ghostnexus.net
|
|
8
|
+
Project-URL: Documentation, https://ghostnexus.net/docs
|
|
9
|
+
Project-URL: Repository, https://github.com/ghostnexus/ghostnexus-python
|
|
10
|
+
Project-URL: Bug Tracker, https://github.com/ghostnexus/ghostnexus-python/issues
|
|
11
|
+
Keywords: gpu,cloud,ai,machine-learning,decentralized,compute
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
Requires-Dist: requests>=2.28
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest; extra == "dev"
|
|
27
|
+
Requires-Dist: responses; extra == "dev"
|
|
28
|
+
Requires-Dist: black; extra == "dev"
|
|
29
|
+
Requires-Dist: mypy; extra == "dev"
|
|
30
|
+
|
|
31
|
+
# GhostNexus Python SDK
|
|
32
|
+
|
|
33
|
+
**Run Python scripts on GDPR-compliant GPU cloud — billed by the second.**
|
|
34
|
+
|
|
35
|
+
[](https://pypi.org/project/ghostnexus/)
|
|
36
|
+
[](https://www.python.org/downloads/)
|
|
37
|
+
[](https://opensource.org/licenses/MIT)
|
|
38
|
+
|
|
39
|
+
GhostNexus is a decentralized GPU compute network. Submit your Python script, our distributed GPUs run it — securely, in the EU, billed by the second.
|
|
40
|
+
|
|
41
|
+
- **RTX 4090** from $0.50/hr · **A100 80GB** from $2.20/hr · **H100** from $3.50/hr
|
|
42
|
+
- GDPR-compliant · EU-hosted · AI Act 2026 ready
|
|
43
|
+
- Docker sandbox isolation · Per-second billing · No minimum deposit
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Install
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install ghostnexus
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Quick Start
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import ghostnexus
|
|
57
|
+
|
|
58
|
+
client = ghostnexus.Client(api_key="gn_live_...")
|
|
59
|
+
|
|
60
|
+
# Submit a script
|
|
61
|
+
job = client.run("train.py")
|
|
62
|
+
|
|
63
|
+
# Wait for result
|
|
64
|
+
result = job.wait()
|
|
65
|
+
print(result.output)
|
|
66
|
+
print(f"Cost: ${result.cost_credits:.4f}")
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Get your API key at [ghostnexus.net/dashboard](https://ghostnexus.net/dashboard) — $15 free credits with code `WELCOME15`.
|
|
70
|
+
|
|
71
|
+
## Examples
|
|
72
|
+
|
|
73
|
+
### Fine-tune a model
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
import ghostnexus
|
|
77
|
+
|
|
78
|
+
client = ghostnexus.Client() # reads GHOSTNEXUS_API_KEY env var
|
|
79
|
+
|
|
80
|
+
job = client.run("finetune_mistral.py", task_name="Mistral-7B LoRA fine-tune")
|
|
81
|
+
result = job.wait(timeout=7200) # 2h max
|
|
82
|
+
|
|
83
|
+
print(result.output)
|
|
84
|
+
# Duration: 1h 23m
|
|
85
|
+
# Cost: $0.69
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Inline script
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
job = client.run(
|
|
92
|
+
"import torch; print(f'CUDA: {torch.cuda.is_available()}')",
|
|
93
|
+
inline=True,
|
|
94
|
+
task_name="cuda-check"
|
|
95
|
+
)
|
|
96
|
+
result = job.wait(timeout=30)
|
|
97
|
+
print(result.output) # CUDA: True
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Check balance
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
balance = client.balance()
|
|
104
|
+
print(f"Available credits: ${balance:.2f}")
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Job history
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
jobs = client.history(limit=10)
|
|
111
|
+
for job in jobs:
|
|
112
|
+
print(f"{job.task_name}: {job.status} — ${job.cost_credits:.4f}")
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Error Handling
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
from ghostnexus.exceptions import InsufficientCreditsError, JobFailedError
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
job = client.run("train.py")
|
|
122
|
+
result = job.wait()
|
|
123
|
+
except InsufficientCreditsError:
|
|
124
|
+
print("Top up at https://ghostnexus.net/dashboard")
|
|
125
|
+
except JobFailedError as e:
|
|
126
|
+
print(f"Job failed. Logs:\n{e.logs}")
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Environment Variable
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
export GHOSTNEXUS_API_KEY="gn_live_..."
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
# No need to pass api_key= explicitly
|
|
137
|
+
client = ghostnexus.Client()
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Pricing
|
|
141
|
+
|
|
142
|
+
| GPU | VRAM | Price | Best for |
|
|
143
|
+
|-----|------|-------|----------|
|
|
144
|
+
| RTX 4070 | 12 GB | $0.25/hr | Inference, small models |
|
|
145
|
+
| RTX 4090 | 24 GB | $0.50/hr | Fine-tuning 7B–13B |
|
|
146
|
+
| RTX A6000 | 48 GB | $0.70/hr | Fine-tuning 30B–70B |
|
|
147
|
+
| A100 80GB | 80 GB | $2.20/hr | Large-scale training |
|
|
148
|
+
| H100 80GB | 80 GB | $3.50/hr | LLM pre-training |
|
|
149
|
+
|
|
150
|
+
Billed **per second** — no hourly rounding, no minimum.
|
|
151
|
+
|
|
152
|
+
## Links
|
|
153
|
+
|
|
154
|
+
- [Dashboard](https://ghostnexus.net/dashboard) — manage jobs and credits
|
|
155
|
+
- [Pricing](https://ghostnexus.net/pricing) — full GPU catalog
|
|
156
|
+
- [Blog](https://ghostnexus.net/blog) — tutorials and guides
|
|
157
|
+
- [Contact](mailto:contact@ghostnexus.net)
|
|
158
|
+
|
|
159
|
+
## License
|
|
160
|
+
|
|
161
|
+
MIT
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
ghostnexus/__init__.py
|
|
4
|
+
ghostnexus/client.py
|
|
5
|
+
ghostnexus/exceptions.py
|
|
6
|
+
ghostnexus/models.py
|
|
7
|
+
ghostnexus.egg-info/PKG-INFO
|
|
8
|
+
ghostnexus.egg-info/SOURCES.txt
|
|
9
|
+
ghostnexus.egg-info/dependency_links.txt
|
|
10
|
+
ghostnexus.egg-info/requires.txt
|
|
11
|
+
ghostnexus.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ghostnexus
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ghostnexus"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "SDK Python officiel pour GhostNexus — GPU cloud décentralisé, sécurisé & RGPD"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
authors = [{ name = "GhostNexus", email = "contact@ghostnexus.net" }]
|
|
12
|
+
keywords = ["gpu", "cloud", "ai", "machine-learning", "decentralized", "compute"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 4 - Beta",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"Intended Audience :: Science/Research",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.9",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
24
|
+
]
|
|
25
|
+
requires-python = ">=3.9"
|
|
26
|
+
dependencies = ["requests>=2.28"]
|
|
27
|
+
|
|
28
|
+
[project.optional-dependencies]
|
|
29
|
+
dev = ["pytest", "responses", "black", "mypy"]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://ghostnexus.net"
|
|
33
|
+
Documentation = "https://ghostnexus.net/docs"
|
|
34
|
+
Repository = "https://github.com/ghostnexus/ghostnexus-python"
|
|
35
|
+
"Bug Tracker" = "https://github.com/ghostnexus/ghostnexus-python/issues"
|
|
36
|
+
|
|
37
|
+
[tool.setuptools.packages.find]
|
|
38
|
+
where = ["."]
|
|
39
|
+
include = ["ghostnexus*"]
|