fix: prevent repeated model GPU submissions

This commit is contained in:
CoolBoy
2026-08-02 18:48:47 +08:00
parent 8e68c6f611
commit ccae7ff8f3
9 changed files with 489 additions and 24 deletions

View File

@@ -21,6 +21,23 @@ class ModelHubAPIError(RuntimeError):
self.payload = payload
def parse_model_submission_precheck(payload: Any) -> dict[str, Any]:
"""Validate the community lookup response instead of failing open."""
if not isinstance(payload, dict):
raise ModelHubAPIError("Community model precheck returned a non-object response", payload=payload)
data = payload.get("data")
if not isinstance(data, dict) or "verifyResult" not in data:
raise ModelHubAPIError("Community model precheck response is incomplete", payload=payload)
verify_result = data.get("verifyResult") or {}
if not isinstance(verify_result, dict):
raise ModelHubAPIError("Community model precheck verifyResult is invalid", payload=payload)
return {
"isInDB": data.get("isInDB") is True,
"processedGpus": set(str(gpu) for gpu in verify_result),
}
class ModelHubClient:
def __init__(
self,
@@ -42,9 +59,13 @@ class ModelHubClient:
retries=retries,
)
def search_by_model_id(self, model_id: str) -> dict[str, Any]:
def search_by_model_id(self, model_id: str, *, force_refresh: bool = False) -> dict[str, Any]:
del force_refresh
return self._request("GET", "/api/computility/models/search-by-model-id", query={"modelId": model_id})
def model_submission_precheck(self, model_id: str, *, force_refresh: bool = False) -> dict[str, Any]:
return parse_model_submission_precheck(self.search_by_model_id(model_id, force_refresh=force_refresh))
def is_model_processed_for_gpu(self, model_id: str, target_gpu: str) -> bool:
gpu_result = self.get_verify_result_map(model_id).get(target_gpu)
if gpu_result is None:
@@ -56,7 +77,7 @@ class ModelHubClient:
return ((payload.get("data") or {}).get("verifyResult") or {})
def processed_gpus_for_model(self, model_id: str) -> set[str]:
return set(self.get_verify_result_map(model_id).keys())
return set(self.model_submission_precheck(model_id)["processedGpus"])
def list_tasks_page(
self,
@@ -256,6 +277,16 @@ DUPLICATE_SUBMISSION_MARKERS = (
"already in progress",
)
MODEL_UNIQUENESS_ERROR_MARKERS = (
"模型唯一性检查",
"唯一性检查没有通过",
"模型已存在",
"model uniqueness",
"uniqueness check",
"duplicate model",
"model already exists",
)
DEFAULT_CAPACITY_STATE_PATH = Path(".modelhub_state/account_capacity.json")
@@ -275,6 +306,13 @@ def is_duplicate_submission_error(error: ModelHubAPIError) -> bool:
return any(marker in message for marker in DUPLICATE_SUBMISSION_MARKERS)
def is_model_uniqueness_error(error: ModelHubAPIError) -> bool:
message = str(error).strip().lower()
if isinstance(error.payload, dict):
message = f"{message} {error.payload.get('message') or ''}".lower()
return any(marker in message for marker in MODEL_UNIQUENESS_ERROR_MARKERS)
class ModelHubClientPool:
def __init__(
self,
@@ -385,13 +423,6 @@ class ModelHubClientPool:
except Exception:
return max(0, max_count - 1)
def _safe_search_by_model_id(self, client: ModelHubClient, model_id: str) -> dict[str, Any]:
try:
payload = client.search_by_model_id(model_id)
return payload if isinstance(payload, dict) else {}
except Exception:
return {}
def _safe_list_tasks(self, client: ModelHubClient, kwargs: dict[str, Any]) -> list[dict[str, Any]]:
try:
return client.list_tasks(**kwargs)
@@ -546,29 +577,33 @@ class ModelHubClientPool:
if now - cached_at >= self._verify_cache_ttl:
self._verify_cache.pop(model_id, None)
def search_by_model_id(self, model_id: str) -> dict[str, Any]:
def search_by_model_id(self, model_id: str, *, force_refresh: bool = False) -> dict[str, Any]:
# Reuse recent model verification results across short poll cycles.
with self._state_lock:
cached = self._read_from_cache(model_id)
if cached is not None:
return cached
if not force_refresh:
with self._state_lock:
cached = self._read_from_cache(model_id)
if cached is not None:
return cached
# Only query ONE client (the reader) instead of fanning out to all clients.
# verifyResult is model-specific platform data, not account-specific.
response = self._safe_search_by_model_id(self._reader, model_id)
response = self._reader.search_by_model_id(model_id)
if not isinstance(response, dict):
response = {"code": 0, "data": {"verifyResult": {}}}
raise ModelHubAPIError("Community model precheck returned a non-object response", payload=response)
with self._state_lock:
self._write_to_cache(model_id, response)
return response
def model_submission_precheck(self, model_id: str, *, force_refresh: bool = False) -> dict[str, Any]:
return parse_model_submission_precheck(self.search_by_model_id(model_id, force_refresh=force_refresh))
def get_verify_result_map(self, model_id: str) -> dict[str, Any]:
payload = self.search_by_model_id(model_id)
return ((payload.get("data") or {}).get("verifyResult") or {})
def processed_gpus_for_model(self, model_id: str) -> set[str]:
return set(self.get_verify_result_map(model_id).keys())
return set(self.model_submission_precheck(model_id)["processedGpus"])
def _reserve_account(self, excluded: set[int]) -> tuple[int, int] | None:
with self._state_lock: