node and account names
This commit is contained in:
@@ -570,7 +570,7 @@ class _NodeEntry:
|
||||
"benchmark_tokens_per_sec", "quantization", "managed_assignment",
|
||||
"model_tokens_per_sec",
|
||||
"pending_directives", "last_heartbeat", "tracker_mode",
|
||||
"relay_addr", "cert_fingerprint", "peer_id",
|
||||
"relay_addr", "cert_fingerprint", "peer_id", "friendly_name",
|
||||
# heartbeat stats (reported by node, cumulative)
|
||||
"total_requests", "failed_requests", "queue_depth", "proxy_inflight", "uptime_seconds",
|
||||
"current_requests",
|
||||
@@ -606,6 +606,7 @@ class _NodeEntry:
|
||||
relay_addr: str | None = None,
|
||||
cert_fingerprint: str | None = None,
|
||||
peer_id: str | None = None,
|
||||
friendly_name: str | None = None,
|
||||
) -> None:
|
||||
self.node_id = node_id
|
||||
self.endpoint = endpoint
|
||||
@@ -632,6 +633,7 @@ class _NodeEntry:
|
||||
self.relay_addr = relay_addr
|
||||
self.cert_fingerprint = cert_fingerprint
|
||||
self.peer_id = peer_id
|
||||
self.friendly_name = friendly_name
|
||||
self.pending_directives: list[dict] = []
|
||||
self.last_heartbeat: float = time.monotonic()
|
||||
self.total_requests: int = 0
|
||||
@@ -2231,6 +2233,22 @@ def _registration_ban_error(contracts: Any | None, wallet_address: str | None) -
|
||||
return None
|
||||
|
||||
|
||||
_MAX_FRIENDLY_NAME_LENGTH = 64
|
||||
|
||||
|
||||
def _normalize_friendly_name(value: object) -> str | None:
|
||||
if value is None:
|
||||
return None
|
||||
if not isinstance(value, str):
|
||||
raise ValueError("friendly_name must be a string")
|
||||
name = value.strip()
|
||||
if not name:
|
||||
return None
|
||||
if len(name) > _MAX_FRIENDLY_NAME_LENGTH:
|
||||
raise ValueError(f"friendly_name must be at most {_MAX_FRIENDLY_NAME_LENGTH} characters")
|
||||
return name
|
||||
|
||||
|
||||
def _tracker_log(
|
||||
server: "_TrackerHTTPServer",
|
||||
level: str,
|
||||
@@ -2606,6 +2624,9 @@ class _TrackerHandler(http.server.BaseHTTPRequestHandler):
|
||||
if self.path == "/v1/auth/logout":
|
||||
self._handle_auth_logout()
|
||||
return
|
||||
if self.path == "/v1/account/profile":
|
||||
self._handle_account_profile()
|
||||
return
|
||||
if self.path == "/v1/account/keys":
|
||||
self._handle_account_key_create()
|
||||
return
|
||||
@@ -2923,6 +2944,7 @@ class _TrackerHandler(http.server.BaseHTTPRequestHandler):
|
||||
"nodes": [
|
||||
{
|
||||
"node_id": node.node_id,
|
||||
"friendly_name": node.friendly_name,
|
||||
"endpoint": node.endpoint,
|
||||
"relay_addr": node.relay_addr,
|
||||
"peer_id": node.peer_id,
|
||||
@@ -4001,6 +4023,11 @@ class _TrackerHandler(http.server.BaseHTTPRequestHandler):
|
||||
relay_addr = body.get("relay_addr") or None
|
||||
cert_fingerprint = body.get("cert_fingerprint") or None
|
||||
peer_id = body.get("peer_id") or None
|
||||
try:
|
||||
friendly_name = _normalize_friendly_name(body.get("friendly_name"))
|
||||
except ValueError as exc:
|
||||
self._send_json(400, {"error": str(exc)})
|
||||
return
|
||||
|
||||
node_id = _node_id_for_registration(
|
||||
endpoint,
|
||||
@@ -4035,6 +4062,7 @@ class _TrackerHandler(http.server.BaseHTTPRequestHandler):
|
||||
relay_addr=relay_addr,
|
||||
cert_fingerprint=cert_fingerprint,
|
||||
peer_id=peer_id,
|
||||
friendly_name=friendly_name,
|
||||
)
|
||||
with server.lock:
|
||||
self._purge_expired_nodes()
|
||||
@@ -4053,6 +4081,12 @@ class _TrackerHandler(http.server.BaseHTTPRequestHandler):
|
||||
for eid in stale_ids:
|
||||
old = server.registry.pop(eid)
|
||||
stale_entries.append((eid, old))
|
||||
if friendly_name is None:
|
||||
for _eid, old in stale_entries:
|
||||
if old.friendly_name:
|
||||
friendly_name = old.friendly_name
|
||||
break
|
||||
entry.friendly_name = friendly_name
|
||||
is_topology_change = node_id not in stale_ids or any(
|
||||
(old.shard_start, old.shard_end) != (entry.shard_start, entry.shard_end)
|
||||
for eid, old in stale_entries
|
||||
@@ -4162,6 +4196,12 @@ class _TrackerHandler(http.server.BaseHTTPRequestHandler):
|
||||
entry.uptime_seconds = float(body["uptime_seconds"])
|
||||
if "status" in body and body["status"] in ("ready", "loading"):
|
||||
entry.status = body["status"]
|
||||
if "friendly_name" in body:
|
||||
try:
|
||||
entry.friendly_name = _normalize_friendly_name(body.get("friendly_name"))
|
||||
except ValueError as exc:
|
||||
self._send_json(400, {"error": str(exc)})
|
||||
return
|
||||
directives = list(entry.pending_directives)
|
||||
entry.pending_directives.clear()
|
||||
new_assignment = entry.pending_new_assignment
|
||||
@@ -4443,6 +4483,7 @@ class _TrackerHandler(http.server.BaseHTTPRequestHandler):
|
||||
email=body.get("email"),
|
||||
wallet=body.get("wallet"),
|
||||
password=str(body.get("password") or ""),
|
||||
nickname=body.get("nickname"),
|
||||
)
|
||||
except ValueError as exc:
|
||||
self._send_json(400, {"error": str(exc)})
|
||||
@@ -4518,6 +4559,27 @@ class _TrackerHandler(http.server.BaseHTTPRequestHandler):
|
||||
"topup_amount": server.devnet_topup_amount if server.billing is not None else 0.0,
|
||||
})
|
||||
|
||||
def _handle_account_profile(self):
|
||||
accounts = self._require_accounts()
|
||||
if accounts is None:
|
||||
return
|
||||
account = self._session_account()
|
||||
if account is None:
|
||||
self._send_json(401, {"error": "login required"})
|
||||
return
|
||||
body = self._read_json_body()
|
||||
if body is None:
|
||||
return
|
||||
try:
|
||||
updated = accounts.update_profile(
|
||||
account["account_id"],
|
||||
nickname=body.get("nickname"),
|
||||
)
|
||||
except ValueError as exc:
|
||||
self._send_json(400, {"error": str(exc)})
|
||||
return
|
||||
self._send_json(200, {"account": updated})
|
||||
|
||||
def _handle_account_key_create(self):
|
||||
accounts = self._require_accounts()
|
||||
if accounts is None:
|
||||
@@ -6076,6 +6138,10 @@ class TrackerServer:
|
||||
shard_end = int(payload["shard_end"]) if payload.get("shard_end") is not None else None
|
||||
except (TypeError, ValueError):
|
||||
return
|
||||
try:
|
||||
friendly_name = _normalize_friendly_name(payload.get("friendly_name"))
|
||||
except ValueError:
|
||||
friendly_name = None
|
||||
entry = _NodeEntry(
|
||||
node_id=node_id,
|
||||
endpoint=endpoint.rstrip("/"),
|
||||
@@ -6095,6 +6161,7 @@ class TrackerServer:
|
||||
if isinstance(payload.get("downloaded_models"), list)
|
||||
else None
|
||||
),
|
||||
friendly_name=_normalize_friendly_name(payload.get("friendly_name")),
|
||||
)
|
||||
with self._lock:
|
||||
self._registry[node_id] = entry
|
||||
|
||||
Reference in New Issue
Block a user