sixseven 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.
- sixseven-0.1.0/PKG-INFO +84 -0
- sixseven-0.1.0/README.md +48 -0
- sixseven-0.1.0/pyproject.toml +68 -0
- sixseven-0.1.0/setup.cfg +4 -0
- sixseven-0.1.0/setup.py +25 -0
- sixseven-0.1.0/src/sixseven.egg-info/PKG-INFO +84 -0
- sixseven-0.1.0/src/sixseven.egg-info/SOURCES.txt +19 -0
- sixseven-0.1.0/src/sixseven.egg-info/dependency_links.txt +1 -0
- sixseven-0.1.0/src/sixseven.egg-info/entry_points.txt +2 -0
- sixseven-0.1.0/src/sixseven.egg-info/requires.txt +10 -0
- sixseven-0.1.0/src/sixseven.egg-info/top_level.txt +1 -0
- sixseven-0.1.0/src/sixtyseven/__init__.py +36 -0
- sixseven-0.1.0/src/sixtyseven/cli.py +64 -0
- sixseven-0.1.0/src/sixtyseven/client.py +190 -0
- sixseven-0.1.0/src/sixtyseven/config.py +161 -0
- sixseven-0.1.0/src/sixtyseven/exceptions.py +40 -0
- sixseven-0.1.0/src/sixtyseven/local.py +335 -0
- sixseven-0.1.0/src/sixtyseven/metrics.py +157 -0
- sixseven-0.1.0/src/sixtyseven/run.py +445 -0
- sixseven-0.1.0/src/sixtyseven/server.py +383 -0
- sixseven-0.1.0/src/sixtyseven/utils.py +171 -0
sixseven-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: sixseven
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for Sixtyseven ML experiment tracking
|
|
5
|
+
Author: Sixtyseven Team
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/sixtyseven/sixtyseven
|
|
8
|
+
Project-URL: Documentation, https://docs.sixtyseven.ai
|
|
9
|
+
Project-URL: Repository, https://github.com/sixtyseven/sixtyseven
|
|
10
|
+
Keywords: ml,machine-learning,experiment-tracking,metrics,training
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
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: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
25
|
+
Requires-Python: >=3.8
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
Requires-Dist: requests>=2.28.0
|
|
28
|
+
Requires-Dist: websocket-client>=1.4.0
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
32
|
+
Requires-Dist: responses>=0.23.0; extra == "dev"
|
|
33
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
34
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
35
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
36
|
+
|
|
37
|
+
# Sixtyseven Python SDK
|
|
38
|
+
|
|
39
|
+
Track ML experiments locally. No server setup required.
|
|
40
|
+
|
|
41
|
+
## Install
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install sixtyseven
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from sixtyseven import Run
|
|
51
|
+
|
|
52
|
+
with Run(project="my-project", name="experiment-1") as run:
|
|
53
|
+
run.log_config({"learning_rate": 0.001, "epochs": 10})
|
|
54
|
+
|
|
55
|
+
for epoch in range(10):
|
|
56
|
+
loss = train_one_epoch()
|
|
57
|
+
run.log_metrics({"loss": loss}, step=epoch)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## View Results
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
sixtyseven --logdir ~/.sixtyseven/logs
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Opens a dashboard at http://localhost:6767
|
|
67
|
+
|
|
68
|
+
## API
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
run.log_metrics({"loss": 0.5, "accuracy": 0.85}, step=epoch) # Log metrics
|
|
72
|
+
run.log_config({"lr": 0.001}) # Log config
|
|
73
|
+
run.add_tags(["baseline"]) # Add tags
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Environment Variables
|
|
77
|
+
|
|
78
|
+
| Variable | Description | Default |
|
|
79
|
+
| ------------------- | ------------------ | -------------------- |
|
|
80
|
+
| `SIXTYSEVEN_LOGDIR` | Where to save logs | `~/.sixtyseven/logs` |
|
|
81
|
+
|
|
82
|
+
## License
|
|
83
|
+
|
|
84
|
+
MIT
|
sixseven-0.1.0/README.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Sixtyseven Python SDK
|
|
2
|
+
|
|
3
|
+
Track ML experiments locally. No server setup required.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install sixtyseven
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from sixtyseven import Run
|
|
15
|
+
|
|
16
|
+
with Run(project="my-project", name="experiment-1") as run:
|
|
17
|
+
run.log_config({"learning_rate": 0.001, "epochs": 10})
|
|
18
|
+
|
|
19
|
+
for epoch in range(10):
|
|
20
|
+
loss = train_one_epoch()
|
|
21
|
+
run.log_metrics({"loss": loss}, step=epoch)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## View Results
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
sixtyseven --logdir ~/.sixtyseven/logs
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Opens a dashboard at http://localhost:6767
|
|
31
|
+
|
|
32
|
+
## API
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
run.log_metrics({"loss": 0.5, "accuracy": 0.85}, step=epoch) # Log metrics
|
|
36
|
+
run.log_config({"lr": 0.001}) # Log config
|
|
37
|
+
run.add_tags(["baseline"]) # Add tags
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Environment Variables
|
|
41
|
+
|
|
42
|
+
| Variable | Description | Default |
|
|
43
|
+
| ------------------- | ------------------ | -------------------- |
|
|
44
|
+
| `SIXTYSEVEN_LOGDIR` | Where to save logs | `~/.sixtyseven/logs` |
|
|
45
|
+
|
|
46
|
+
## License
|
|
47
|
+
|
|
48
|
+
MIT
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=69,<77", "wheel", "packaging>=23.2"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "sixseven"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python SDK for Sixtyseven ML experiment tracking"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.8"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Sixtyseven Team" }
|
|
14
|
+
]
|
|
15
|
+
keywords = ["ml", "machine-learning", "experiment-tracking", "metrics", "training"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Intended Audience :: Science/Research",
|
|
20
|
+
"Operating System :: OS Independent",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.8",
|
|
23
|
+
"Programming Language :: Python :: 3.9",
|
|
24
|
+
"Programming Language :: Python :: 3.10",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"Programming Language :: Python :: 3.13",
|
|
28
|
+
"Programming Language :: Python :: 3.14",
|
|
29
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
30
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
dependencies = [
|
|
34
|
+
"requests>=2.28.0",
|
|
35
|
+
"websocket-client>=1.4.0",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.optional-dependencies]
|
|
39
|
+
dev = [
|
|
40
|
+
"pytest>=7.0.0",
|
|
41
|
+
"pytest-asyncio>=0.21.0",
|
|
42
|
+
"responses>=0.23.0",
|
|
43
|
+
"black>=23.0.0",
|
|
44
|
+
"ruff>=0.1.0",
|
|
45
|
+
"mypy>=1.0.0",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
[project.urls]
|
|
49
|
+
Homepage = "https://github.com/sixtyseven/sixtyseven"
|
|
50
|
+
Documentation = "https://docs.sixtyseven.ai"
|
|
51
|
+
Repository = "https://github.com/sixtyseven/sixtyseven"
|
|
52
|
+
|
|
53
|
+
[project.scripts]
|
|
54
|
+
sixtyseven = "sixtyseven.cli:main"
|
|
55
|
+
|
|
56
|
+
[tool.setuptools]
|
|
57
|
+
package-dir = {"" = "src"}
|
|
58
|
+
packages = ["sixtyseven"]
|
|
59
|
+
include-package-data = true
|
|
60
|
+
|
|
61
|
+
[tool.setuptools.package-data]
|
|
62
|
+
sixtyseven = ["bin/**"]
|
|
63
|
+
|
|
64
|
+
[tool.mypy]
|
|
65
|
+
python_version = "3.8"
|
|
66
|
+
warn_return_any = true
|
|
67
|
+
warn_unused_configs = true
|
|
68
|
+
ignore_missing_imports = true
|
sixseven-0.1.0/setup.cfg
ADDED
sixseven-0.1.0/setup.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from setuptools import setup
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
from wheel.bdist_wheel import bdist_wheel as _bdist_wheel
|
|
8
|
+
|
|
9
|
+
class bdist_wheel(_bdist_wheel):
|
|
10
|
+
def finalize_options(self) -> None:
|
|
11
|
+
super().finalize_options()
|
|
12
|
+
self.root_is_pure = False
|
|
13
|
+
if hasattr(self, "root_is_purelib"):
|
|
14
|
+
self.root_is_purelib = False
|
|
15
|
+
|
|
16
|
+
def get_tag(self) -> tuple[str, str, str]:
|
|
17
|
+
_, _, plat = super().get_tag()
|
|
18
|
+
# py3-none-<platform>: works with any Python 3, no ABI, platform-specific
|
|
19
|
+
return "py3", "none", plat
|
|
20
|
+
|
|
21
|
+
except Exception: # wheel not available
|
|
22
|
+
bdist_wheel = None # type: ignore[assignment]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
setup(cmdclass={"bdist_wheel": bdist_wheel} if bdist_wheel else {})
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: sixseven
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for Sixtyseven ML experiment tracking
|
|
5
|
+
Author: Sixtyseven Team
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/sixtyseven/sixtyseven
|
|
8
|
+
Project-URL: Documentation, https://docs.sixtyseven.ai
|
|
9
|
+
Project-URL: Repository, https://github.com/sixtyseven/sixtyseven
|
|
10
|
+
Keywords: ml,machine-learning,experiment-tracking,metrics,training
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
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: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
25
|
+
Requires-Python: >=3.8
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
Requires-Dist: requests>=2.28.0
|
|
28
|
+
Requires-Dist: websocket-client>=1.4.0
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
32
|
+
Requires-Dist: responses>=0.23.0; extra == "dev"
|
|
33
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
34
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
35
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
36
|
+
|
|
37
|
+
# Sixtyseven Python SDK
|
|
38
|
+
|
|
39
|
+
Track ML experiments locally. No server setup required.
|
|
40
|
+
|
|
41
|
+
## Install
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install sixtyseven
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from sixtyseven import Run
|
|
51
|
+
|
|
52
|
+
with Run(project="my-project", name="experiment-1") as run:
|
|
53
|
+
run.log_config({"learning_rate": 0.001, "epochs": 10})
|
|
54
|
+
|
|
55
|
+
for epoch in range(10):
|
|
56
|
+
loss = train_one_epoch()
|
|
57
|
+
run.log_metrics({"loss": loss}, step=epoch)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## View Results
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
sixtyseven --logdir ~/.sixtyseven/logs
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Opens a dashboard at http://localhost:6767
|
|
67
|
+
|
|
68
|
+
## API
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
run.log_metrics({"loss": 0.5, "accuracy": 0.85}, step=epoch) # Log metrics
|
|
72
|
+
run.log_config({"lr": 0.001}) # Log config
|
|
73
|
+
run.add_tags(["baseline"]) # Add tags
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Environment Variables
|
|
77
|
+
|
|
78
|
+
| Variable | Description | Default |
|
|
79
|
+
| ------------------- | ------------------ | -------------------- |
|
|
80
|
+
| `SIXTYSEVEN_LOGDIR` | Where to save logs | `~/.sixtyseven/logs` |
|
|
81
|
+
|
|
82
|
+
## License
|
|
83
|
+
|
|
84
|
+
MIT
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
setup.py
|
|
4
|
+
src/sixseven.egg-info/PKG-INFO
|
|
5
|
+
src/sixseven.egg-info/SOURCES.txt
|
|
6
|
+
src/sixseven.egg-info/dependency_links.txt
|
|
7
|
+
src/sixseven.egg-info/entry_points.txt
|
|
8
|
+
src/sixseven.egg-info/requires.txt
|
|
9
|
+
src/sixseven.egg-info/top_level.txt
|
|
10
|
+
src/sixtyseven/__init__.py
|
|
11
|
+
src/sixtyseven/cli.py
|
|
12
|
+
src/sixtyseven/client.py
|
|
13
|
+
src/sixtyseven/config.py
|
|
14
|
+
src/sixtyseven/exceptions.py
|
|
15
|
+
src/sixtyseven/local.py
|
|
16
|
+
src/sixtyseven/metrics.py
|
|
17
|
+
src/sixtyseven/run.py
|
|
18
|
+
src/sixtyseven/server.py
|
|
19
|
+
src/sixtyseven/utils.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sixtyseven
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Sixtyseven - ML Experiment Tracking SDK
|
|
3
|
+
|
|
4
|
+
A Python SDK for tracking machine learning experiments with Sixtyseven.
|
|
5
|
+
|
|
6
|
+
Example usage:
|
|
7
|
+
from sixtyseven import Run
|
|
8
|
+
|
|
9
|
+
with Run(project="my-team/image-classifier") as run:
|
|
10
|
+
run.log_config({"learning_rate": 0.001})
|
|
11
|
+
|
|
12
|
+
for epoch in range(100):
|
|
13
|
+
loss = train()
|
|
14
|
+
run.log_metrics({"loss": loss}, step=epoch)
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from sixtyseven.config import configure
|
|
18
|
+
from sixtyseven.exceptions import (
|
|
19
|
+
APIError,
|
|
20
|
+
AuthenticationError,
|
|
21
|
+
ServerError,
|
|
22
|
+
SixtySevenError,
|
|
23
|
+
ValidationError,
|
|
24
|
+
)
|
|
25
|
+
from sixtyseven.run import Run
|
|
26
|
+
|
|
27
|
+
__version__ = "0.1.0"
|
|
28
|
+
__all__ = [
|
|
29
|
+
"Run",
|
|
30
|
+
"configure",
|
|
31
|
+
"SixtySevenError",
|
|
32
|
+
"AuthenticationError",
|
|
33
|
+
"APIError",
|
|
34
|
+
"ValidationError",
|
|
35
|
+
"ServerError",
|
|
36
|
+
]
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Console entrypoint for the bundled sixtyseven CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import platform
|
|
7
|
+
import shutil
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _platform_id() -> Optional[str]:
|
|
14
|
+
system = platform.system().lower()
|
|
15
|
+
machine = platform.machine().lower()
|
|
16
|
+
|
|
17
|
+
if machine in {"x86_64", "amd64"}:
|
|
18
|
+
arch = "amd64"
|
|
19
|
+
elif machine in {"aarch64", "arm64"}:
|
|
20
|
+
arch = "arm64"
|
|
21
|
+
else:
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
if system == "darwin":
|
|
25
|
+
return f"darwin-{arch}"
|
|
26
|
+
if system == "linux":
|
|
27
|
+
return f"linux-{arch}"
|
|
28
|
+
if system == "windows":
|
|
29
|
+
return f"windows-{arch}"
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _bundled_binary_path() -> Optional[str]:
|
|
34
|
+
platform_id = _platform_id()
|
|
35
|
+
if not platform_id:
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
binary_name = "sixtyseven.exe" if platform.system() == "Windows" else "sixtyseven"
|
|
39
|
+
base_dir = Path(__file__).resolve().parent
|
|
40
|
+
candidate = base_dir / "bin" / platform_id / binary_name
|
|
41
|
+
if candidate.is_file() and os.access(candidate, os.X_OK):
|
|
42
|
+
return str(candidate)
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _find_binary() -> Optional[str]:
|
|
47
|
+
bundled = _bundled_binary_path()
|
|
48
|
+
if bundled:
|
|
49
|
+
return bundled
|
|
50
|
+
|
|
51
|
+
binary_name = "sixtyseven.exe" if platform.system() == "Windows" else "sixtyseven"
|
|
52
|
+
return shutil.which(binary_name)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def main() -> None:
|
|
56
|
+
binary = _find_binary()
|
|
57
|
+
if not binary:
|
|
58
|
+
print(
|
|
59
|
+
"Could not find 'sixtyseven' binary. Reinstall the package or set SIXTYSEVEN_BINARY.",
|
|
60
|
+
file=sys.stderr,
|
|
61
|
+
)
|
|
62
|
+
raise SystemExit(1)
|
|
63
|
+
|
|
64
|
+
os.execv(binary, [binary, *sys.argv[1:]])
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"""HTTP client for communicating with the Sixtyseven API."""
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
from typing import Any, Dict, List, Optional
|
|
5
|
+
from urllib.parse import urljoin
|
|
6
|
+
|
|
7
|
+
import requests
|
|
8
|
+
|
|
9
|
+
from sixtyseven.config import SDKConfig
|
|
10
|
+
from sixtyseven.exceptions import APIError, AuthenticationError
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SixtySevenClient:
|
|
14
|
+
"""HTTP client for the Sixtyseven API."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, config: SDKConfig, api_key: Optional[str] = None):
|
|
17
|
+
"""
|
|
18
|
+
Initialize the client.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
config: SDK configuration
|
|
22
|
+
api_key: API key (overrides config)
|
|
23
|
+
"""
|
|
24
|
+
self.config = config
|
|
25
|
+
self.api_key = api_key or config.api_key
|
|
26
|
+
self.session = requests.Session()
|
|
27
|
+
|
|
28
|
+
if self.api_key:
|
|
29
|
+
self.session.headers["Authorization"] = f"Bearer {self.api_key}"
|
|
30
|
+
|
|
31
|
+
self.session.headers["Content-Type"] = "application/json"
|
|
32
|
+
self.session.headers["User-Agent"] = "sixtyseven-python/0.1.0"
|
|
33
|
+
|
|
34
|
+
def _url(self, path: str) -> str:
|
|
35
|
+
"""Build full URL for an API endpoint."""
|
|
36
|
+
return urljoin(self.config.base_url, f"/api/v1{path}")
|
|
37
|
+
|
|
38
|
+
def _request(
|
|
39
|
+
self,
|
|
40
|
+
method: str,
|
|
41
|
+
path: str,
|
|
42
|
+
data: Optional[Dict[str, Any]] = None,
|
|
43
|
+
params: Optional[Dict[str, Any]] = None,
|
|
44
|
+
) -> Dict[str, Any]:
|
|
45
|
+
"""
|
|
46
|
+
Make an HTTP request with retry logic.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
method: HTTP method
|
|
50
|
+
path: API path
|
|
51
|
+
data: Request body data
|
|
52
|
+
params: Query parameters
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Response JSON data
|
|
56
|
+
|
|
57
|
+
Raises:
|
|
58
|
+
AuthenticationError: If authentication fails
|
|
59
|
+
APIError: If the request fails
|
|
60
|
+
"""
|
|
61
|
+
url = self._url(path)
|
|
62
|
+
last_error = None
|
|
63
|
+
|
|
64
|
+
for attempt in range(self.config.retry_count):
|
|
65
|
+
try:
|
|
66
|
+
response = self.session.request(
|
|
67
|
+
method=method,
|
|
68
|
+
url=url,
|
|
69
|
+
json=data,
|
|
70
|
+
params=params,
|
|
71
|
+
timeout=self.config.timeout,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
if response.status_code == 401:
|
|
75
|
+
raise AuthenticationError("Invalid API key")
|
|
76
|
+
|
|
77
|
+
if response.status_code == 403:
|
|
78
|
+
raise AuthenticationError("Insufficient permissions")
|
|
79
|
+
|
|
80
|
+
if response.status_code >= 400:
|
|
81
|
+
error_data = response.json() if response.text else {}
|
|
82
|
+
raise APIError(
|
|
83
|
+
error_data.get(
|
|
84
|
+
"error",
|
|
85
|
+
f"Request failed with status {response.status_code}",
|
|
86
|
+
),
|
|
87
|
+
status_code=response.status_code,
|
|
88
|
+
response=error_data,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
if response.text:
|
|
92
|
+
return response.json()
|
|
93
|
+
return {}
|
|
94
|
+
|
|
95
|
+
except requests.exceptions.RequestException as e:
|
|
96
|
+
last_error = e
|
|
97
|
+
if attempt < self.config.retry_count - 1:
|
|
98
|
+
time.sleep(self.config.retry_delay * (attempt + 1))
|
|
99
|
+
continue
|
|
100
|
+
|
|
101
|
+
raise APIError(
|
|
102
|
+
f"Request failed after {self.config.retry_count} attempts: {last_error}"
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
def create_run(
|
|
106
|
+
self,
|
|
107
|
+
team_slug: str,
|
|
108
|
+
app_slug: str,
|
|
109
|
+
name: Optional[str] = None,
|
|
110
|
+
tags: Optional[List[str]] = None,
|
|
111
|
+
config: Optional[Dict[str, Any]] = None,
|
|
112
|
+
git_info: Optional[Dict[str, Any]] = None,
|
|
113
|
+
system_info: Optional[Dict[str, Any]] = None,
|
|
114
|
+
) -> str:
|
|
115
|
+
"""
|
|
116
|
+
Create a new run.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
The run ID
|
|
120
|
+
"""
|
|
121
|
+
data = {
|
|
122
|
+
"name": name,
|
|
123
|
+
"tags": tags or [],
|
|
124
|
+
"config": config or {},
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if git_info:
|
|
128
|
+
data["git_info"] = git_info
|
|
129
|
+
if system_info:
|
|
130
|
+
data["system_info"] = system_info
|
|
131
|
+
|
|
132
|
+
response = self._request(
|
|
133
|
+
"POST",
|
|
134
|
+
f"/teams/{team_slug}/apps/{app_slug}/runs",
|
|
135
|
+
data=data,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
return response["id"]
|
|
139
|
+
|
|
140
|
+
def update_run_status(
|
|
141
|
+
self,
|
|
142
|
+
run_id: str,
|
|
143
|
+
status: str,
|
|
144
|
+
error: Optional[str] = None,
|
|
145
|
+
) -> None:
|
|
146
|
+
"""Update run status (completed, failed, aborted)."""
|
|
147
|
+
data = {"status": status}
|
|
148
|
+
if error:
|
|
149
|
+
data["error_message"] = error
|
|
150
|
+
|
|
151
|
+
self._request("PUT", f"/runs/{run_id}/status", data=data)
|
|
152
|
+
|
|
153
|
+
def update_run_config(self, run_id: str, config: Dict[str, Any]) -> None:
|
|
154
|
+
"""Merge new config with existing run config."""
|
|
155
|
+
self._request("PUT", f"/runs/{run_id}/config", data=config)
|
|
156
|
+
|
|
157
|
+
def batch_log_metrics(
|
|
158
|
+
self,
|
|
159
|
+
run_id: str,
|
|
160
|
+
metrics: List[Dict[str, Any]],
|
|
161
|
+
) -> None:
|
|
162
|
+
"""
|
|
163
|
+
Log a batch of metrics.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
run_id: The run ID
|
|
167
|
+
metrics: List of metric dictionaries with name, value, step, timestamp
|
|
168
|
+
"""
|
|
169
|
+
self._request(
|
|
170
|
+
"POST",
|
|
171
|
+
f"/runs/{run_id}/metrics",
|
|
172
|
+
data={"metrics": metrics},
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
def add_run_tags(self, run_id: str, tags: List[str]) -> None:
|
|
176
|
+
"""Add tags to a run."""
|
|
177
|
+
# This would need to be implemented via the update endpoint
|
|
178
|
+
pass
|
|
179
|
+
|
|
180
|
+
def upload_artifact(
|
|
181
|
+
self,
|
|
182
|
+
run_id: str,
|
|
183
|
+
path: str,
|
|
184
|
+
name: Optional[str] = None,
|
|
185
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
186
|
+
) -> None:
|
|
187
|
+
"""Upload an artifact file."""
|
|
188
|
+
# Artifact upload would need multipart form handling
|
|
189
|
+
# For now, this is a placeholder
|
|
190
|
+
pass
|