runbios-sdk 0.2.2__tar.gz → 0.2.4__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.
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/PKG-INFO +1 -1
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/pyproject.toml +1 -1
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/src/bios/_client.py +23 -1
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/src/bios/datasets.py +2 -4
- runbios_sdk-0.2.4/tests/test_workspace_resolution.py +59 -0
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/.gitignore +0 -0
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/LICENSE +0 -0
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/README.md +0 -0
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/src/bios/__init__.py +0 -0
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/src/bios/_gpu_priorities.py +0 -0
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/src/bios/_types.py +0 -0
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/src/bios/errors.py +0 -0
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/src/bios/gpu.py +0 -0
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/src/bios/inference.py +0 -0
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/src/bios/integrations.py +0 -0
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/src/bios/loop.py +0 -0
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/src/bios/models.py +0 -0
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/src/bios/py.typed +0 -0
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/src/bios/training.py +0 -0
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/src/bios/wallet.py +0 -0
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/src/runbios/__init__.py +0 -0
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/tests/test_capacity_contract.py +0 -0
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/tests/test_coming_soon.py +0 -0
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/tests/test_contracts.py +0 -0
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/tests/test_inference.py +0 -0
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/tests/test_loop.py +0 -0
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/tests/test_loop_training.py +0 -0
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/tests/test_release_metadata.py +0 -0
- {runbios_sdk-0.2.2 → runbios_sdk-0.2.4}/tests/test_sdk_contract_drift.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: runbios-sdk
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: Official Python SDK for the Run BiOS training and deployment platform API
|
|
5
5
|
Project-URL: Homepage, https://runbios.ai
|
|
6
6
|
Project-URL: Documentation, https://runbios.ai/docs/sdk-python
|
|
@@ -5,7 +5,7 @@ from typing import Any, Dict, Optional, Tuple
|
|
|
5
5
|
|
|
6
6
|
import requests
|
|
7
7
|
|
|
8
|
-
VERSION = "0.2.
|
|
8
|
+
VERSION = "0.2.4"
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class ApiError(Exception):
|
|
@@ -120,6 +120,28 @@ class HttpClient:
|
|
|
120
120
|
self._session = requests.Session()
|
|
121
121
|
self._session.headers.update(self._build_base_headers())
|
|
122
122
|
|
|
123
|
+
def resolve_workspace_id(self) -> str:
|
|
124
|
+
"""Return the configured workspace or the API key's bound workspace.
|
|
125
|
+
|
|
126
|
+
Multipart dataset endpoints require workspace_id in the form body while
|
|
127
|
+
ordinary JSON routes can rely on gateway identity headers. A bound API
|
|
128
|
+
key must therefore remain sufficient for upload: introspect once and
|
|
129
|
+
cache the authoritative binding instead of requiring a duplicate ID.
|
|
130
|
+
"""
|
|
131
|
+
if self._workspace_id:
|
|
132
|
+
return self._workspace_id
|
|
133
|
+
data = self.get("/api/api-keys/introspect")
|
|
134
|
+
workspace = data.get("workspace", {}) if isinstance(data, dict) else {}
|
|
135
|
+
workspace_id = workspace.get("id") if isinstance(workspace, dict) else None
|
|
136
|
+
if not isinstance(workspace_id, str) or not workspace_id.strip():
|
|
137
|
+
raise ValueError(
|
|
138
|
+
"Run BiOS: the API key is not bound to a workspace; "
|
|
139
|
+
"dataset operations require a workspace-bound key"
|
|
140
|
+
)
|
|
141
|
+
self._workspace_id = workspace_id.strip()
|
|
142
|
+
self._session.headers["X-Workspace-ID"] = self._workspace_id
|
|
143
|
+
return self._workspace_id
|
|
144
|
+
|
|
123
145
|
def _build_base_headers(self) -> Dict[str, str]:
|
|
124
146
|
headers: Dict[str, str] = {
|
|
125
147
|
"User-Agent": "bios-python/{}".format(VERSION),
|
|
@@ -145,7 +145,7 @@ class Datasets:
|
|
|
145
145
|
|
|
146
146
|
ws = workspace_id or self._http._workspace_id
|
|
147
147
|
if not ws:
|
|
148
|
-
|
|
148
|
+
ws = self._http.resolve_workspace_id()
|
|
149
149
|
with open(file_path, "rb") as f:
|
|
150
150
|
files = {"file": (file_name, f, content_type)}
|
|
151
151
|
data: Dict[str, str] = {"name": name}
|
|
@@ -323,9 +323,7 @@ class Datasets:
|
|
|
323
323
|
"""Register a Hub source for background validation and optional materialization."""
|
|
324
324
|
ws = workspace_id or self._http._workspace_id
|
|
325
325
|
if not ws:
|
|
326
|
-
|
|
327
|
-
"Run BiOS: workspace_id is required to register a HuggingFace dataset"
|
|
328
|
-
)
|
|
326
|
+
ws = self._http.resolve_workspace_id()
|
|
329
327
|
body: Dict[str, Any] = {
|
|
330
328
|
"hf_dataset_id": repo_id,
|
|
331
329
|
"name": name or repo_id.split("/")[-1],
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import tempfile
|
|
3
|
+
import unittest
|
|
4
|
+
from unittest.mock import Mock
|
|
5
|
+
|
|
6
|
+
from bios._client import HttpClient
|
|
7
|
+
from bios.datasets import Datasets
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class WorkspaceResolutionTests(unittest.TestCase):
|
|
11
|
+
def test_http_client_introspects_once_and_caches_bound_workspace(self) -> None:
|
|
12
|
+
client = HttpClient(api_key="bios-test", base_url="https://api.test")
|
|
13
|
+
client.get = Mock(return_value={"workspace": {"id": "ws-from-key"}}) # type: ignore[method-assign]
|
|
14
|
+
|
|
15
|
+
self.assertEqual(client.resolve_workspace_id(), "ws-from-key")
|
|
16
|
+
self.assertEqual(client.resolve_workspace_id(), "ws-from-key")
|
|
17
|
+
client.get.assert_called_once_with("/api/api-keys/introspect")
|
|
18
|
+
self.assertEqual(client._session.headers["X-Workspace-ID"], "ws-from-key")
|
|
19
|
+
|
|
20
|
+
def test_dataset_upload_uses_resolved_workspace_when_not_explicit(self) -> None:
|
|
21
|
+
http = Mock()
|
|
22
|
+
http._workspace_id = None
|
|
23
|
+
http.resolve_workspace_id.return_value = "ws-from-key"
|
|
24
|
+
http.upload.return_value = {"id": "ds-1", "name": "sample", "status": "processing"}
|
|
25
|
+
datasets = Datasets(http)
|
|
26
|
+
|
|
27
|
+
with tempfile.NamedTemporaryFile(suffix=".jsonl", delete=False) as handle:
|
|
28
|
+
handle.write(b'{"messages":[{"role":"user","content":"hi"},{"role":"assistant","content":"hello"}]}\n')
|
|
29
|
+
path = handle.name
|
|
30
|
+
try:
|
|
31
|
+
result = datasets.upload(path, "sample")
|
|
32
|
+
finally:
|
|
33
|
+
os.unlink(path)
|
|
34
|
+
|
|
35
|
+
self.assertEqual(result["id"], "ds-1")
|
|
36
|
+
http.resolve_workspace_id.assert_called_once_with()
|
|
37
|
+
data = http.upload.call_args.kwargs["data"]
|
|
38
|
+
self.assertEqual(data["workspace_id"], "ws-from-key")
|
|
39
|
+
|
|
40
|
+
def test_explicit_workspace_does_not_introspect(self) -> None:
|
|
41
|
+
http = Mock()
|
|
42
|
+
http._workspace_id = None
|
|
43
|
+
http.upload.return_value = {"id": "ds-1", "name": "sample", "status": "processing"}
|
|
44
|
+
datasets = Datasets(http)
|
|
45
|
+
|
|
46
|
+
with tempfile.NamedTemporaryFile(suffix=".jsonl", delete=False) as handle:
|
|
47
|
+
handle.write(b'{"messages":[{"role":"user","content":"hi"},{"role":"assistant","content":"hello"}]}\n')
|
|
48
|
+
path = handle.name
|
|
49
|
+
try:
|
|
50
|
+
datasets.upload(path, "sample", workspace_id="ws-explicit")
|
|
51
|
+
finally:
|
|
52
|
+
os.unlink(path)
|
|
53
|
+
|
|
54
|
+
http.resolve_workspace_id.assert_not_called()
|
|
55
|
+
self.assertEqual(http.upload.call_args.kwargs["data"]["workspace_id"], "ws-explicit")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
if __name__ == "__main__":
|
|
59
|
+
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|