feat: reserve queue capacity for recent models
This commit is contained in:
@@ -33,6 +33,7 @@ from modelhub_client import (
|
||||
ModelHubAPIError,
|
||||
ModelHubClient,
|
||||
ModelHubClientPool,
|
||||
OldModelQueuePolicyError,
|
||||
is_duplicate_submission_error,
|
||||
is_model_uniqueness_error,
|
||||
)
|
||||
@@ -107,6 +108,18 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
default=0,
|
||||
help="Maximum tasks to submit in one run (0 means unlimited)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--old-model-queue-threshold",
|
||||
type=int,
|
||||
default=int(os.getenv("MODELHUB_OLD_MODEL_QUEUE_THRESHOLD", "80")),
|
||||
help=argparse.SUPPRESS,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--recent-model-days",
|
||||
type=int,
|
||||
default=int(os.getenv("MODELHUB_RECENT_MODEL_DAYS", "7")),
|
||||
help=argparse.SUPPRESS,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--disable-candidate-preflight",
|
||||
action="store_true",
|
||||
@@ -548,9 +561,17 @@ def build_adaptive_scan_stages(
|
||||
initial_updated_after,
|
||||
initial_limit: int,
|
||||
max_models: int = ADAPTIVE_SCAN_MAX_MODELS,
|
||||
allow_older_than_recent_window: bool = True,
|
||||
recent_model_days: int = 7,
|
||||
) -> list[dict[str, Any]]:
|
||||
max_models = max(1, min(ADAPTIVE_SCAN_MAX_MODELS, int(max_models)))
|
||||
initial_limit = min(max_models, max(1, int(initial_limit)))
|
||||
recent_cutoff = now - timedelta(days=max(1, int(recent_model_days)))
|
||||
if not allow_older_than_recent_window and (
|
||||
initial_updated_after is None or initial_updated_after < recent_cutoff
|
||||
):
|
||||
initial_updated_after = recent_cutoff
|
||||
|
||||
stages: list[dict[str, Any]] = [
|
||||
{
|
||||
"name": "configured_window",
|
||||
@@ -559,17 +580,16 @@ def build_adaptive_scan_stages(
|
||||
}
|
||||
]
|
||||
|
||||
seven_days_ago = now - timedelta(days=7)
|
||||
thirty_days_ago = now - timedelta(days=30)
|
||||
if initial_updated_after is not None and initial_updated_after > seven_days_ago:
|
||||
if initial_updated_after is not None and initial_updated_after > recent_cutoff:
|
||||
stages.append(
|
||||
{
|
||||
"name": "last_7_days",
|
||||
"updatedAfter": seven_days_ago,
|
||||
"updatedAfter": recent_cutoff,
|
||||
"limit": min(max_models, max(initial_limit, ADAPTIVE_SCAN_MIN_FALLBACK_MODELS)),
|
||||
}
|
||||
)
|
||||
if initial_updated_after is not None and initial_updated_after > thirty_days_ago:
|
||||
if allow_older_than_recent_window and initial_updated_after is not None and initial_updated_after > thirty_days_ago:
|
||||
stages.append(
|
||||
{
|
||||
"name": "last_30_days",
|
||||
@@ -577,7 +597,7 @@ def build_adaptive_scan_stages(
|
||||
"limit": min(max_models, max(initial_limit, 1500)),
|
||||
}
|
||||
)
|
||||
if initial_updated_after is not None or initial_limit < max_models:
|
||||
if allow_older_than_recent_window and (initial_updated_after is not None or initial_limit < max_models):
|
||||
stages.append(
|
||||
{
|
||||
"name": "all_history",
|
||||
@@ -598,6 +618,13 @@ def build_adaptive_scan_stages(
|
||||
return deduped
|
||||
|
||||
|
||||
def is_recent_model(last_modified: Any, *, reference_time, recent_model_days: int = 7) -> bool:
|
||||
parsed = parse_datetime(last_modified)
|
||||
if parsed is None:
|
||||
return False
|
||||
return parsed >= reference_time - timedelta(days=max(1, int(recent_model_days)))
|
||||
|
||||
|
||||
def collect_candidates_from_models(
|
||||
*,
|
||||
models: list[HFModelSummary],
|
||||
@@ -702,7 +729,14 @@ def submit_candidate(
|
||||
payload["strategyId"] = strategy_id
|
||||
submit_time = utc_now()
|
||||
try:
|
||||
response = modelhub_client.add_task(payload)
|
||||
if hasattr(modelhub_client, "add_task_for_model"):
|
||||
response = modelhub_client.add_task_for_model(
|
||||
payload,
|
||||
model_last_modified=candidate.get("lastModified"),
|
||||
submitted_at=submit_time,
|
||||
)
|
||||
else:
|
||||
response = modelhub_client.add_task(payload)
|
||||
task_id = extract_task_id_from_submit_response(response)
|
||||
if task_id is None:
|
||||
task_id = modelhub_client.find_recent_task_id(candidate["repoId"], candidate["targetGpu"], submit_time)
|
||||
@@ -714,6 +748,12 @@ def submit_candidate(
|
||||
"responseData": response.get("data"),
|
||||
}
|
||||
except ModelHubAPIError as exc:
|
||||
if isinstance(exc, OldModelQueuePolicyError):
|
||||
return {
|
||||
"outcome": "old_model_policy_skipped",
|
||||
"candidate": candidate,
|
||||
"reason": "all_accounts_at_old_model_queue_threshold",
|
||||
}
|
||||
if is_model_uniqueness_error(exc):
|
||||
print(
|
||||
f"[submit] skipped repo={candidate['repoId']} gpu={candidate['targetGpu']} "
|
||||
@@ -799,6 +839,8 @@ def run_submission(
|
||||
clients,
|
||||
capacity_probe_interval_cycles=max(0, int(getattr(args, "capacity_probe_interval_cycles", 3) or 0)),
|
||||
capacity_state_path=Path(getattr(args, "capacity_state_path", DEFAULT_CAPACITY_STATE_PATH)),
|
||||
old_model_queue_threshold=max(1, int(getattr(args, "old_model_queue_threshold", 80) or 80)),
|
||||
recent_model_days=max(1, int(getattr(args, "recent_model_days", 7) or 7)),
|
||||
)
|
||||
|
||||
if hasattr(modelhub_client, "begin_cycle"):
|
||||
@@ -1065,11 +1107,25 @@ def run_submission(
|
||||
|
||||
pipeline_tags = pipeline_tags_for_task_types(selected_task_types)
|
||||
explicit_scan_cap = max(0, int(getattr(args, "max_scan_models", 0) or 0))
|
||||
recent_model_days = max(1, int(getattr(args, "recent_model_days", 7) or 7))
|
||||
old_model_queue_threshold = max(1, int(getattr(args, "old_model_queue_threshold", 80) or 80))
|
||||
old_model_slots_before_scan: int | None = None
|
||||
if hasattr(modelhub_client, "old_model_submit_slots"):
|
||||
old_model_slots_before_scan = int(modelhub_client.old_model_submit_slots())
|
||||
allow_older_models_for_scan = old_model_slots_before_scan is None or old_model_slots_before_scan > 0
|
||||
print(
|
||||
f"[age-policy] recent_days={recent_model_days} account_threshold={old_model_queue_threshold} "
|
||||
f"old_model_slots={old_model_slots_before_scan if old_model_slots_before_scan is not None else 'n/a'} "
|
||||
f"scan_older={'on' if allow_older_models_for_scan else 'off'}",
|
||||
flush=True,
|
||||
)
|
||||
for stage in build_adaptive_scan_stages(
|
||||
now=now,
|
||||
initial_updated_after=updated_after,
|
||||
initial_limit=scan_limit,
|
||||
max_models=explicit_scan_cap or ADAPTIVE_SCAN_MAX_MODELS,
|
||||
allow_older_than_recent_window=allow_older_models_for_scan,
|
||||
recent_model_days=recent_model_days,
|
||||
):
|
||||
if candidate_goal <= 0 or len(candidates) >= candidate_goal:
|
||||
break
|
||||
@@ -1088,6 +1144,16 @@ def run_submission(
|
||||
)
|
||||
except TypeError:
|
||||
models = hf_discovery.list_recent_models(**query_kwargs)
|
||||
if not allow_older_models_for_scan:
|
||||
models = [
|
||||
model
|
||||
for model in models
|
||||
if is_recent_model(
|
||||
model.last_modified,
|
||||
reference_time=now,
|
||||
recent_model_days=recent_model_days,
|
||||
)
|
||||
]
|
||||
|
||||
stage_candidates, stage_skipped, stage_failed, processed_count = collect_candidates_from_models(
|
||||
models=models,
|
||||
@@ -1165,11 +1231,25 @@ def run_submission(
|
||||
# already-scanned pool until the desired number of real submissions is
|
||||
# reached or account capacity is genuinely exhausted.
|
||||
while len(submitted) < target_submit_count and len(attempted_candidates) < max_submit_attempts:
|
||||
allow_older_models_now = (
|
||||
bool(modelhub_client.can_submit_old_models())
|
||||
if hasattr(modelhub_client, "can_submit_old_models")
|
||||
else True
|
||||
)
|
||||
submission_reference_time = utc_now()
|
||||
remaining_candidates = [
|
||||
candidate
|
||||
for candidate in diversified_candidates
|
||||
if candidate_key(candidate) not in attempted_keys
|
||||
and not submission_exclusion_store.is_blocked(candidate["repoId"], candidate["targetGpu"])
|
||||
and (
|
||||
allow_older_models_now
|
||||
or is_recent_model(
|
||||
candidate.get("lastModified"),
|
||||
reference_time=submission_reference_time,
|
||||
recent_model_days=recent_model_days,
|
||||
)
|
||||
)
|
||||
]
|
||||
remaining_candidates = one_candidate_per_model(remaining_candidates)
|
||||
desired_count = min(
|
||||
@@ -1209,6 +1289,7 @@ def run_submission(
|
||||
batch_submitted_candidates: list[dict[str, Any]] = []
|
||||
batch_duplicate_candidates: list[dict[str, Any]] = []
|
||||
batch_uniqueness_rejected_candidates: list[dict[str, Any]] = []
|
||||
batch_policy_skipped_candidates: list[dict[str, Any]] = []
|
||||
batch_failed_candidates: list[dict[str, Any]] = []
|
||||
for index in range(len(batch_candidates)):
|
||||
result = ordered_results.get(index)
|
||||
@@ -1243,6 +1324,16 @@ def run_submission(
|
||||
}
|
||||
)
|
||||
continue
|
||||
if result["outcome"] == "old_model_policy_skipped":
|
||||
batch_policy_skipped_candidates.append(candidate)
|
||||
skipped.append(
|
||||
{
|
||||
"repoId": candidate["repoId"],
|
||||
"targetGpu": candidate["targetGpu"],
|
||||
"reason": "all_accounts_at_old_model_queue_threshold",
|
||||
}
|
||||
)
|
||||
continue
|
||||
if result["outcome"] in {"failed", "precheck_deferred"}:
|
||||
batch_failed_candidates.append(candidate)
|
||||
failed.append(
|
||||
@@ -1290,7 +1381,7 @@ def run_submission(
|
||||
claim_store.mark_submitted(
|
||||
[*batch_submitted_candidates, *batch_duplicate_candidates, *batch_uniqueness_rejected_candidates]
|
||||
)
|
||||
claim_store.release(batch_failed_candidates)
|
||||
claim_store.release([*batch_failed_candidates, *batch_policy_skipped_candidates])
|
||||
if strategy_manager is not None:
|
||||
strategy_manager.record_accepted(batch_submitted_candidates)
|
||||
|
||||
@@ -1332,6 +1423,12 @@ def run_submission(
|
||||
"gpuStrategy": strategy_summary,
|
||||
"marketIntelligence": market_summary,
|
||||
"candidatePreflight": preflight_summary,
|
||||
"agePolicy": {
|
||||
"recentModelDays": recent_model_days,
|
||||
"oldModelSubmitThreshold": old_model_queue_threshold,
|
||||
"oldModelSlotsBeforeScan": old_model_slots_before_scan,
|
||||
"olderHistoryScanEnabled": allow_older_models_for_scan,
|
||||
},
|
||||
"scanLimit": scan_limit,
|
||||
"candidateGoal": candidate_goal,
|
||||
"scanStages": scan_stages,
|
||||
|
||||
Reference in New Issue
Block a user