mirror of
https://github.com/hanzoai/python-sdk.git
synced 2026-10-23 20:23:00 +00:00
hanzo-train 0.1.1 drives the training wire at /v1/train/clients.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"""Tinker-shaped client for the Hanzo Engine training API.
|
||||
"""Tinker-shaped client for Hanzo's training API, ``/v1/train/clients``.
|
||||
|
||||
The engine exposes a small LoRA training surface under ``/v1/training``. This
|
||||
api.hanzo.ai and a self-hosted engine both serve it there. This
|
||||
client mirrors the shapes of Thinking Machines' ``tinker`` SDK -- so training
|
||||
loops written against tinker port across unchanged -- using Hanzo-canonical
|
||||
field names and a single synchronous HTTP path (httpx, no retries).
|
||||
@@ -208,7 +208,7 @@ class ServiceClient:
|
||||
cfg = lora_config or LoraConfig()
|
||||
data = self._request(
|
||||
"POST",
|
||||
"/v1/training/clients",
|
||||
"/v1/train/clients",
|
||||
{
|
||||
"base_model": base_model,
|
||||
"lora_config": {
|
||||
@@ -224,7 +224,7 @@ class ServiceClient:
|
||||
return tc
|
||||
|
||||
def list_training_clients(self) -> list[TrainingClientInfo]:
|
||||
data = self._request("GET", "/v1/training/clients")
|
||||
data = self._request("GET", "/v1/train/clients")
|
||||
return [_info(c) for c in data["clients"]]
|
||||
|
||||
def close(self) -> None:
|
||||
@@ -240,7 +240,7 @@ class TrainingClient:
|
||||
self._poll_interval = poll_interval
|
||||
|
||||
def _path(self, suffix: str = "") -> str:
|
||||
return f"/v1/training/clients/{self.id}{suffix}"
|
||||
return f"/v1/train/clients/{self.id}{suffix}"
|
||||
|
||||
def forward_backward(
|
||||
self, data: Iterable[Datum | dict[str, Any]]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "hanzo-train"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
description = "Hanzo Train — Tinker-shaped client for the Hanzo Engine training API"
|
||||
readme = "README.md"
|
||||
license = { text = "Apache-2.0" }
|
||||
|
||||
@@ -58,7 +58,7 @@ def test_create_defaults_and_no_wait() -> None:
|
||||
|
||||
assert tc.id == "tc-abc"
|
||||
assert seen["method"] == "POST"
|
||||
assert seen["path"] == "/v1/training/clients"
|
||||
assert seen["path"] == "/v1/train/clients"
|
||||
assert seen["body"]["base_model"] == "m"
|
||||
lora = seen["body"]["lora_config"]
|
||||
assert lora["rank"] == 16
|
||||
@@ -132,7 +132,7 @@ def test_forward_backward_prompt_form() -> None:
|
||||
tc = TrainingClient(service(handler), "tc-abc")
|
||||
out = tc.forward_backward([{"prompt": "2+2=", "completion": "4"}]).result()
|
||||
|
||||
assert seen["path"] == "/v1/training/clients/tc-abc/forward_backward"
|
||||
assert seen["path"] == "/v1/train/clients/tc-abc/forward_backward"
|
||||
assert seen["body"]["data"] == [{"prompt": "2+2=", "completion": "4"}]
|
||||
assert out.loss == 1.5
|
||||
assert out.num_tokens == 3
|
||||
@@ -194,7 +194,7 @@ def test_optim_step_explicit_params() -> None:
|
||||
tc = TrainingClient(service(handler), "tc-abc")
|
||||
res = tc.optim_step(AdamParams(lr=1e-4)).result()
|
||||
|
||||
assert seen["path"] == "/v1/training/clients/tc-abc/optim_step"
|
||||
assert seen["path"] == "/v1/train/clients/tc-abc/optim_step"
|
||||
assert seen["body"]["adam_params"] == {
|
||||
"lr": 1e-4,
|
||||
"beta1": 0.9,
|
||||
@@ -232,7 +232,7 @@ def test_sample_prompt_form() -> None:
|
||||
prompt="2+2=", sampling_params=SamplingParams(max_tokens=8, temperature=0.0), num_samples=1
|
||||
).result()
|
||||
|
||||
assert seen["path"] == "/v1/training/clients/tc-abc/sample"
|
||||
assert seen["path"] == "/v1/train/clients/tc-abc/sample"
|
||||
assert seen["body"]["prompt"] == "2+2="
|
||||
assert "tokens" not in seen["body"]
|
||||
assert seen["body"]["num_samples"] == 1
|
||||
@@ -284,7 +284,7 @@ def test_save_weights_and_alias() -> None:
|
||||
tc = TrainingClient(service(handler), "tc-abc")
|
||||
saved = tc.save_weights_and_get_sampling_client(name="my-adapter").result()
|
||||
|
||||
assert seen["path"] == "/v1/training/clients/tc-abc/save_weights"
|
||||
assert seen["path"] == "/v1/train/clients/tc-abc/save_weights"
|
||||
assert seen["body"] == {"name": "my-adapter"}
|
||||
assert saved.path == "/data/adapters/my-adapter"
|
||||
assert saved.format == "peft"
|
||||
@@ -297,7 +297,7 @@ def test_save_weights_and_alias() -> None:
|
||||
def test_get_info() -> None:
|
||||
def handler(request: httpx.Request) -> httpx.Response:
|
||||
assert request.method == "GET"
|
||||
assert request.url.path == "/v1/training/clients/tc-abc"
|
||||
assert request.url.path == "/v1/train/clients/tc-abc"
|
||||
return httpx.Response(
|
||||
200,
|
||||
json=info_json(
|
||||
@@ -325,7 +325,7 @@ def test_get_info() -> None:
|
||||
def test_list_training_clients() -> None:
|
||||
def handler(request: httpx.Request) -> httpx.Response:
|
||||
assert request.method == "GET"
|
||||
assert request.url.path == "/v1/training/clients"
|
||||
assert request.url.path == "/v1/train/clients"
|
||||
return httpx.Response(
|
||||
200,
|
||||
json={
|
||||
@@ -355,7 +355,7 @@ def test_delete() -> None:
|
||||
|
||||
assert result is None
|
||||
assert seen["method"] == "DELETE"
|
||||
assert seen["path"] == "/v1/training/clients/tc-abc"
|
||||
assert seen["path"] == "/v1/train/clients/tc-abc"
|
||||
|
||||
|
||||
def test_error_404() -> None:
|
||||
|
||||
Reference in New Issue
Block a user