feat: add durable success-first modelhub agent
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import os
|
||||
from collections import Counter
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
@@ -10,7 +11,8 @@ from typing import Any
|
||||
|
||||
from common import parse_datetime, runtime_instance_id, utc_now, write_json, write_jsonl
|
||||
from candidate_preflight import CandidatePreflightAdvisor
|
||||
from gpu_strategy import DEFAULT_GPU_STRATEGY_PATH, GPUStrategyManager
|
||||
from config_optimizer import SafeConfigOptimizer
|
||||
from gpu_strategy import DEFAULT_GPU_STRATEGY_PATH
|
||||
from hf_discovery import HuggingFaceDiscovery
|
||||
from history_stats import (
|
||||
append_ledger_entry,
|
||||
@@ -38,10 +40,23 @@ from modelhub_client import (
|
||||
is_model_uniqueness_error,
|
||||
)
|
||||
from models import CandidateModel, HFModelSummary, ModelInspection
|
||||
from official_capabilities import (
|
||||
DEFAULT_OFFICIAL_CAPABILITIES_PATH,
|
||||
OfficialCapabilityRegistry,
|
||||
OfficialCapabilityUnavailable,
|
||||
)
|
||||
from outcome_tracker import DEFAULT_OUTCOMES_PATH, OutcomeTracker
|
||||
from routing_engine import DEFAULT_ROUTING_STATE_PATH, SuccessFirstRoutingEngine
|
||||
from submission_claims import DEFAULT_CLAIMS_PATH, SubmissionClaimStore, candidate_key, diversify_candidates
|
||||
from submission_exclusions import DEFAULT_SUBMISSION_EXCLUSIONS_PATH, SubmissionExclusionStore
|
||||
from task_registry import TASK_SPEC_BY_TYPE, all_task_types, compatible_frameworks_for_task, pipeline_tags_for_task_types, task_specs_for_model
|
||||
from task_registry import (
|
||||
TASK_SPEC_BY_TYPE,
|
||||
all_task_types,
|
||||
compatible_frameworks_for_task,
|
||||
pipeline_tags_for_task_types,
|
||||
register_dynamic_task_route,
|
||||
task_specs_for_model,
|
||||
)
|
||||
from template_selector import TemplateSelector
|
||||
|
||||
|
||||
@@ -212,6 +227,19 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
default=os.getenv("MODELHUB_GPU_STRATEGY_STATE_PATH", str(DEFAULT_GPU_STRATEGY_PATH)),
|
||||
help=argparse.SUPPRESS,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--routing-state-path",
|
||||
default=os.getenv("MODELHUB_ROUTING_STATE_PATH", str(DEFAULT_ROUTING_STATE_PATH)),
|
||||
help=argparse.SUPPRESS,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--official-capabilities-path",
|
||||
default=os.getenv(
|
||||
"MODELHUB_OFFICIAL_CAPABILITIES_PATH",
|
||||
str(DEFAULT_OFFICIAL_CAPABILITIES_PATH),
|
||||
),
|
||||
help=argparse.SUPPRESS,
|
||||
)
|
||||
parser.add_argument("--gpu-strategy-recent-window", type=int, default=1000, help=argparse.SUPPRESS)
|
||||
parser.add_argument("--gpu-strategy-min-long-samples", type=int, default=100, help=argparse.SUPPRESS)
|
||||
parser.add_argument(
|
||||
@@ -311,6 +339,8 @@ def choose_candidate_for_gpu(
|
||||
target_gpu: str,
|
||||
market_intelligence: MarketIntelligenceManager | None = None,
|
||||
preflight_advisor: CandidatePreflightAdvisor | None = None,
|
||||
require_official_config: bool = False,
|
||||
config_optimizer: SafeConfigOptimizer | None = None,
|
||||
) -> CandidateModel | None:
|
||||
candidate, _reason = choose_candidate_for_gpu_detailed(
|
||||
model=model,
|
||||
@@ -320,6 +350,8 @@ def choose_candidate_for_gpu(
|
||||
target_gpu=target_gpu,
|
||||
market_intelligence=market_intelligence,
|
||||
preflight_advisor=preflight_advisor,
|
||||
require_official_config=require_official_config,
|
||||
config_optimizer=config_optimizer,
|
||||
)
|
||||
return candidate
|
||||
|
||||
@@ -333,6 +365,8 @@ def choose_candidate_for_gpu_detailed(
|
||||
target_gpu: str,
|
||||
market_intelligence: MarketIntelligenceManager | None = None,
|
||||
preflight_advisor: CandidatePreflightAdvisor | None = None,
|
||||
require_official_config: bool = False,
|
||||
config_optimizer: SafeConfigOptimizer | None = None,
|
||||
) -> tuple[CandidateModel | None, str | None]:
|
||||
last_rejection_reason: str | None = None
|
||||
for task_type in task_types:
|
||||
@@ -371,6 +405,11 @@ def choose_candidate_for_gpu_detailed(
|
||||
)
|
||||
if config_params is not None:
|
||||
template_id = f"modelhub-live-{task_type}-{framework}-{target_gpu}".lower().replace("_", "-")
|
||||
elif market_intelligence is not None and require_official_config:
|
||||
# A live-discovered route without a valid official config is a
|
||||
# critical capability gap, not permission to use a stale hand template.
|
||||
last_rejection_reason = "official_build_config_unavailable"
|
||||
continue
|
||||
else:
|
||||
try:
|
||||
template = template_selector.select_template(task_type, framework, target_gpu)
|
||||
@@ -388,6 +427,19 @@ def choose_candidate_for_gpu_detailed(
|
||||
)
|
||||
score = float(framework_metadata.get("frameworkCombinedScore") or 0.0)
|
||||
warnings: list[str] = []
|
||||
config_optimization: dict[str, Any] = {"source": "official", "applied": False}
|
||||
if config_optimizer is not None and config_params is not None:
|
||||
config_params, config_optimization = config_optimizer.optimize(
|
||||
task_type=task_type,
|
||||
target_gpu=target_gpu,
|
||||
framework=framework,
|
||||
official_config=config_params,
|
||||
official_lower_bound=float(
|
||||
framework_metadata.get("frameworkMarketWilsonLowerBound") or 0.0
|
||||
),
|
||||
)
|
||||
if config_optimization.get("applied"):
|
||||
warnings.append("validated_success_config_patch_applied")
|
||||
if bool(framework_metadata.get("frameworkEvidenceQualified", False)):
|
||||
warnings.append("framework_selected_from_success_evidence")
|
||||
if config_params is not None and bool(framework_metadata.get("frameworkOfficialConfigValid", False)):
|
||||
@@ -407,6 +459,20 @@ def choose_candidate_for_gpu_detailed(
|
||||
config_params = assessment.config_params
|
||||
warnings.extend(assessment.warnings)
|
||||
preflight_metadata = assessment.metadata
|
||||
preflight_metadata = {
|
||||
**preflight_metadata,
|
||||
"configOptimization": config_optimization,
|
||||
"modelscopeParams": model.params,
|
||||
"modelscopeFileSize": model.file_size,
|
||||
"modelscopeTasks": list(model.tasks),
|
||||
"modelscopeTags": list(model.tags),
|
||||
"modelscopeLicense": model.license,
|
||||
"modelCard": dict(inspection.model_card_metadata),
|
||||
}
|
||||
repository_size = inspection.repository_size_bytes
|
||||
if model.file_size and repository_size and model.file_size > repository_size:
|
||||
warnings.append("modelscope_published_size_used_as_conservative_upper_bound")
|
||||
preflight_metadata["conservativeRepositorySizeBytes"] = model.file_size
|
||||
spec = TASK_SPEC_BY_TYPE[task_type]
|
||||
return CandidateModel(
|
||||
repo_id=model.repo_id,
|
||||
@@ -471,9 +537,15 @@ def process_model_for_candidates(
|
||||
submission_exclusion_store: SubmissionExclusionStore | None = None,
|
||||
market_intelligence: MarketIntelligenceManager | None = None,
|
||||
preflight_advisor: CandidatePreflightAdvisor | None = None,
|
||||
official_registry: OfficialCapabilityRegistry | None = None,
|
||||
config_optimizer: SafeConfigOptimizer | None = None,
|
||||
allow_dynamic_tasks: bool = False,
|
||||
) -> tuple[list[dict[str, Any]], list[dict[str, Any]], list[dict[str, Any]]]:
|
||||
if model.private or model.gated:
|
||||
reason = "modelscope_private" if model.private else "modelscope_gated"
|
||||
return [], [{"repoId": model.repo_id, "reason": reason}], []
|
||||
specs = [spec for spec in task_specs_for_model(model) if spec.task_type in allowed_task_types]
|
||||
if not specs:
|
||||
if not specs and official_registry is None:
|
||||
return [], [{"repoId": model.repo_id, "reason": f"unsupported_pipeline_tag:{model.pipeline_tag or 'unknown'}"}], []
|
||||
|
||||
try:
|
||||
@@ -494,7 +566,8 @@ def process_model_for_candidates(
|
||||
allowed_task_types_set = [spec.task_type for spec in specs]
|
||||
pending_task_types_by_gpu: list[tuple[str, list[str]]] = []
|
||||
|
||||
for target_gpu in target_gpus:
|
||||
route_target_gpus = target_gpus[:6] if official_registry is not None else target_gpus
|
||||
for target_gpu in route_target_gpus:
|
||||
if submission_exclusion_store is not None and submission_exclusion_store.is_blocked(model.repo_id, target_gpu):
|
||||
skipped.append(
|
||||
{"repoId": model.repo_id, "targetGpu": target_gpu, "reason": "model_gpu_uniqueness_blocklist"}
|
||||
@@ -507,11 +580,34 @@ def process_model_for_candidates(
|
||||
skipped.append({"repoId": model.repo_id, "targetGpu": target_gpu, "reason": "local_failure_cooldown_24h"})
|
||||
continue
|
||||
|
||||
compatible_task_types = [
|
||||
task_type
|
||||
for task_type in allowed_task_types_set
|
||||
if template_selector.supported_frameworks_for_auto(task_type, target_gpu)
|
||||
]
|
||||
exact_task_types = list(allowed_task_types_set)
|
||||
if official_registry is not None:
|
||||
exact_task_types = official_registry.task_types_for(
|
||||
modelhub_client,
|
||||
model_address=model.model_address,
|
||||
model_last_modified=model.last_modified.isoformat() if model.last_modified else None,
|
||||
gpu=target_gpu,
|
||||
)
|
||||
route_task_types = (
|
||||
list(exact_task_types)
|
||||
if allow_dynamic_tasks
|
||||
else [task_type for task_type in allowed_task_types_set if task_type in exact_task_types]
|
||||
)
|
||||
compatible_task_types = []
|
||||
for task_type in route_task_types:
|
||||
register_dynamic_task_route(task_type, model.pipeline_tag or task_type)
|
||||
if market_intelligence is not None and not market_intelligence.has_framework_route(task_type, target_gpu):
|
||||
try:
|
||||
market_intelligence.ensure_framework_route(modelhub_client, task_type, target_gpu)
|
||||
except Exception as exc:
|
||||
raise OfficialCapabilityUnavailable(
|
||||
f"framework capability unavailable for {task_type}/{target_gpu}: {type(exc).__name__}: {exc}"
|
||||
) from exc
|
||||
if template_selector.supported_frameworks_for_auto(task_type, target_gpu):
|
||||
compatible_task_types.append(task_type)
|
||||
continue
|
||||
if market_intelligence is not None and market_intelligence.has_framework_route(task_type, target_gpu):
|
||||
compatible_task_types.append(task_type)
|
||||
if not compatible_task_types:
|
||||
skipped.append({"repoId": model.repo_id, "targetGpu": target_gpu, "reason": "no_compatible_auto_template_or_framework"})
|
||||
continue
|
||||
@@ -534,6 +630,8 @@ def process_model_for_candidates(
|
||||
target_gpu=target_gpu,
|
||||
market_intelligence=market_intelligence,
|
||||
preflight_advisor=preflight_advisor,
|
||||
require_official_config=official_registry is not None,
|
||||
config_optimizer=config_optimizer,
|
||||
)
|
||||
if best is None:
|
||||
reason = (
|
||||
@@ -550,6 +648,9 @@ def process_model_for_candidates(
|
||||
if market_intelligence is not None:
|
||||
record.update(market_intelligence.gpu_metadata(target_gpu))
|
||||
record.update(market_intelligence.framework_metadata(best.task_type, target_gpu, best.framework))
|
||||
optimization = (record.get("preflightMetadata") or {}).get("configOptimization") or {}
|
||||
if optimization.get("applied"):
|
||||
record["frameworkConfigSource"] = "official_plus_learned_patch"
|
||||
candidates.append(record)
|
||||
|
||||
return candidates, skipped, failed
|
||||
@@ -640,6 +741,9 @@ def collect_candidates_from_models(
|
||||
read_concurrency: int,
|
||||
market_intelligence: MarketIntelligenceManager | None = None,
|
||||
preflight_advisor: CandidatePreflightAdvisor | None = None,
|
||||
official_registry: OfficialCapabilityRegistry | None = None,
|
||||
config_optimizer: SafeConfigOptimizer | None = None,
|
||||
allow_dynamic_tasks: bool = False,
|
||||
) -> tuple[list[dict[str, Any]], list[dict[str, Any]], list[dict[str, Any]], int]:
|
||||
candidates: list[dict[str, Any]] = []
|
||||
skipped: list[dict[str, Any]] = []
|
||||
@@ -670,6 +774,9 @@ def collect_candidates_from_models(
|
||||
submission_exclusion_store=submission_exclusion_store,
|
||||
market_intelligence=market_intelligence,
|
||||
preflight_advisor=preflight_advisor,
|
||||
official_registry=official_registry,
|
||||
config_optimizer=config_optimizer,
|
||||
allow_dynamic_tasks=allow_dynamic_tasks,
|
||||
): index
|
||||
for index, model in enumerate(chunk)
|
||||
}
|
||||
@@ -679,6 +786,8 @@ def collect_candidates_from_models(
|
||||
model = chunk[index]
|
||||
try:
|
||||
ordered_results[index] = future.result()
|
||||
except OfficialCapabilityUnavailable:
|
||||
raise
|
||||
except Exception as exc:
|
||||
ordered_results[index] = ([], [], [{"repoId": model.repo_id, "reason": str(exc)}])
|
||||
|
||||
@@ -830,10 +939,9 @@ def run_submission(
|
||||
now = now or utc_now()
|
||||
template_selector = template_selector or TemplateSelector()
|
||||
selected_task_types = resolve_task_types(args)
|
||||
target_gpus = resolve_target_gpus(args, template_selector, selected_task_types)
|
||||
if not target_gpus:
|
||||
configured_target_gpus = resolve_target_gpus(args, template_selector, selected_task_types)
|
||||
if not configured_target_gpus:
|
||||
raise RuntimeError("No auto-submittable GPUs are available for the selected task types")
|
||||
submission_target_gpus = list(target_gpus)
|
||||
|
||||
hf_discovery = hf_discovery or HuggingFaceDiscovery(base_url=args.hf_base_url)
|
||||
if modelhub_client is None:
|
||||
@@ -848,6 +956,42 @@ def run_submission(
|
||||
recent_model_days=max(1, int(getattr(args, "recent_model_days", 7) or 7)),
|
||||
)
|
||||
|
||||
official_registry: OfficialCapabilityRegistry | None = None
|
||||
official_summary: dict[str, Any] = {"enabled": False}
|
||||
official_capable = all(
|
||||
hasattr(modelhub_client, name)
|
||||
for name in ("list_machine_info", "list_task_levels", "list_model_task_types")
|
||||
)
|
||||
if official_capable:
|
||||
official_registry = OfficialCapabilityRegistry(
|
||||
Path(
|
||||
getattr(
|
||||
args,
|
||||
"official_capabilities_path",
|
||||
DEFAULT_OFFICIAL_CAPABILITIES_PATH,
|
||||
)
|
||||
)
|
||||
)
|
||||
official_registry.prepare(
|
||||
modelhub_client,
|
||||
fallback_gpus=configured_target_gpus,
|
||||
task_types=selected_task_types,
|
||||
now=now,
|
||||
)
|
||||
if not official_registry.ready:
|
||||
raise OfficialCapabilityUnavailable(
|
||||
official_registry.pause_reason or "critical_official_signal_unavailable"
|
||||
)
|
||||
requested_gpus = configured_target_gpus if (getattr(args, "gpu", None) or getattr(args, "gpus", None)) else None
|
||||
target_gpus = official_registry.eligible_gpus(requested_gpus)
|
||||
official_summary = official_registry.summary()
|
||||
else:
|
||||
# Compatibility path for injected test clients and offline dry-runs.
|
||||
target_gpus = list(configured_target_gpus)
|
||||
if not target_gpus:
|
||||
raise OfficialCapabilityUnavailable("official catalog contains no eligible GPU")
|
||||
submission_target_gpus = list(target_gpus)
|
||||
|
||||
if hasattr(modelhub_client, "begin_cycle"):
|
||||
modelhub_client.begin_cycle()
|
||||
|
||||
@@ -874,7 +1018,11 @@ def run_submission(
|
||||
preflight_summary: dict[str, Any] = (
|
||||
preflight_advisor.summary() if preflight_advisor is not None else {"enabled": False}
|
||||
)
|
||||
strategy_manager: GPUStrategyManager | None = None
|
||||
config_optimizer = SafeConfigOptimizer(
|
||||
intents_path=Path(".modelhub_state/recovery_intents.jsonl"),
|
||||
outcomes_path=Path(args.outcomes_path),
|
||||
)
|
||||
strategy_manager: SuccessFirstRoutingEngine | None = None
|
||||
strategy_summary: dict[str, Any] = {"enabled": False}
|
||||
market_intelligence: MarketIntelligenceManager | None = None
|
||||
market_summary: dict[str, Any] = {"enabled": False}
|
||||
@@ -1042,6 +1190,14 @@ def run_submission(
|
||||
except Exception:
|
||||
market_intelligence.set_local_outcome_stats(None)
|
||||
market_summary = market_intelligence.summary()
|
||||
framework_state = (market_intelligence.state or {}).get("frameworkStats") or {}
|
||||
framework_updated_at = parse_datetime(market_summary.get("frameworkUpdatedAt"))
|
||||
framework_cache_expired = (
|
||||
framework_updated_at is None
|
||||
or now - framework_updated_at > timedelta(hours=24)
|
||||
)
|
||||
if market_summary.get("frameworkError") and (not framework_state or framework_cache_expired):
|
||||
raise OfficialCapabilityUnavailable("critical official framework catalog is unavailable")
|
||||
submission_target_gpus = market_intelligence.eligible_gpus(target_gpus)
|
||||
market_summary["eligibleGpus"] = submission_target_gpus
|
||||
market_summary["shadowOnlyGpus"] = [gpu for gpu in target_gpus if gpu not in submission_target_gpus]
|
||||
@@ -1052,19 +1208,20 @@ def run_submission(
|
||||
)
|
||||
|
||||
if strategy_enabled:
|
||||
strategy_manager = GPUStrategyManager(
|
||||
Path(getattr(args, "gpu_strategy_state_path", DEFAULT_GPU_STRATEGY_PATH)),
|
||||
refresh_submissions=max(1, int(getattr(args, "gpu_strategy_refresh_submissions", 200) or 200)),
|
||||
recent_terminal_window=max(1, int(getattr(args, "gpu_strategy_recent_window", 1000) or 1000)),
|
||||
long_term_min_samples=max(1, int(getattr(args, "gpu_strategy_min_long_samples", 100) or 100)),
|
||||
market_intelligence=market_intelligence,
|
||||
)
|
||||
strategy_history_records = outcome_tracker.get_strategy_history_records()
|
||||
strategy_manager.prepare(
|
||||
modelhub_client,
|
||||
supported_gpus=target_gpus,
|
||||
now=now,
|
||||
history_records=strategy_history_records,
|
||||
routing_path = getattr(args, "routing_state_path", None)
|
||||
legacy_strategy_path = getattr(args, "gpu_strategy_state_path", None)
|
||||
if (
|
||||
not routing_path
|
||||
or (
|
||||
str(routing_path) == str(DEFAULT_ROUTING_STATE_PATH)
|
||||
and legacy_strategy_path
|
||||
and str(legacy_strategy_path) != str(DEFAULT_GPU_STRATEGY_PATH)
|
||||
)
|
||||
):
|
||||
routing_path = legacy_strategy_path or DEFAULT_ROUTING_STATE_PATH
|
||||
strategy_manager = SuccessFirstRoutingEngine(
|
||||
Path(routing_path),
|
||||
outcome_stats=outcome_tracker.get_stats_report(),
|
||||
)
|
||||
strategy_summary = strategy_manager.summary()
|
||||
|
||||
@@ -1117,6 +1274,11 @@ def run_submission(
|
||||
candidate_goal = max(submission_goal, submission_goal * attempt_multiplier)
|
||||
|
||||
pipeline_tags = pipeline_tags_for_task_types(selected_task_types)
|
||||
if official_registry is not None and not getattr(args, "task_types", None):
|
||||
for task_name in official_registry.state.get("discoveredTaskTypes") or []:
|
||||
normalized = str(task_name or "").strip()
|
||||
if normalized and normalized not in pipeline_tags:
|
||||
pipeline_tags.append(normalized)
|
||||
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))
|
||||
recent_model_reserve_slots = max(0, int(getattr(args, "recent_model_reserve_slots", 5) or 0))
|
||||
@@ -1197,6 +1359,9 @@ def run_submission(
|
||||
read_concurrency=max(1, args.read_concurrency),
|
||||
market_intelligence=market_intelligence,
|
||||
preflight_advisor=preflight_advisor,
|
||||
official_registry=official_registry,
|
||||
config_optimizer=config_optimizer,
|
||||
allow_dynamic_tasks=not bool(getattr(args, "task_types", None)),
|
||||
)
|
||||
candidates.extend(stage_candidates)
|
||||
skipped.extend(stage_skipped)
|
||||
@@ -1239,6 +1404,8 @@ def run_submission(
|
||||
claim_store: SubmissionClaimStore | None = None
|
||||
attempted_candidates: list[dict[str, Any]] = []
|
||||
submit_workers = 1
|
||||
state_sync = getattr(args, "_state_sync_manager", None)
|
||||
state_sync_paused = False
|
||||
|
||||
if args.dry_run:
|
||||
attempted_candidates = diversified_candidates[:target_submit_count]
|
||||
@@ -1270,12 +1437,23 @@ def run_submission(
|
||||
target_submit_count - len(submitted),
|
||||
max_submit_attempts - len(attempted_candidates),
|
||||
)
|
||||
if state_sync is not None:
|
||||
desired_count = min(desired_count, int(state_sync.batch_size))
|
||||
batch_candidates = claim_store.claim(remaining_candidates, limit=desired_count)
|
||||
if not batch_candidates:
|
||||
break
|
||||
attempted_candidates.extend(batch_candidates)
|
||||
attempted_keys.update(candidate_key(candidate) for candidate in batch_candidates)
|
||||
|
||||
state_batch_id: str | None = None
|
||||
if state_sync is not None:
|
||||
state_batch_id = state_sync.begin_batch(batch_candidates)
|
||||
if state_batch_id is None:
|
||||
claim_store.release(batch_candidates)
|
||||
state_sync_paused = True
|
||||
print("[cycle] paused reason=state_sync_unhealthy phase=intent", flush=True)
|
||||
break
|
||||
|
||||
batch_workers = resolve_submit_concurrency(
|
||||
args,
|
||||
modelhub_client=modelhub_client,
|
||||
@@ -1389,7 +1567,13 @@ def run_submission(
|
||||
task_type=candidate["taskType"],
|
||||
task_id=result["taskId"],
|
||||
submit_time=result["submitTime"],
|
||||
model_profile=candidate.get("preflightMetadata") or {},
|
||||
model_profile={
|
||||
**dict(candidate.get("preflightMetadata") or {}),
|
||||
"configFingerprint": hashlib.sha256(
|
||||
str(candidate.get("configParams") or "").encode("utf-8")
|
||||
).hexdigest(),
|
||||
"configSource": candidate.get("frameworkConfigSource") or "official",
|
||||
},
|
||||
)
|
||||
|
||||
claim_store.mark_submitted(
|
||||
@@ -1398,6 +1582,17 @@ def run_submission(
|
||||
claim_store.release([*batch_failed_candidates, *batch_policy_skipped_candidates])
|
||||
if strategy_manager is not None:
|
||||
strategy_manager.record_accepted(batch_submitted_candidates)
|
||||
outcome_tracker.save()
|
||||
if state_sync is not None and state_batch_id is not None:
|
||||
ordered_batch_results = [
|
||||
ordered_results[index]
|
||||
for index in range(len(batch_candidates))
|
||||
if index in ordered_results
|
||||
]
|
||||
if not state_sync.finish_batch(state_batch_id, ordered_batch_results):
|
||||
state_sync_paused = True
|
||||
print("[cycle] paused reason=state_sync_unhealthy phase=result", flush=True)
|
||||
break
|
||||
|
||||
if hasattr(modelhub_client, "available_submit_slots") and modelhub_client.available_submit_slots() <= 0:
|
||||
break
|
||||
@@ -1435,6 +1630,7 @@ def run_submission(
|
||||
"historyArchivePath": str(history_archive_path),
|
||||
"historyArchiveRecordCount": len(archived_history),
|
||||
"gpuStrategy": strategy_summary,
|
||||
"officialCapabilities": official_summary,
|
||||
"marketIntelligence": market_summary,
|
||||
"candidatePreflight": preflight_summary,
|
||||
"agePolicy": {
|
||||
@@ -1471,6 +1667,12 @@ def run_submission(
|
||||
"warnings": report.get("warnings", []),
|
||||
"runDir": str(run_dir),
|
||||
"outcomeSyncCount": synced_count,
|
||||
"stateSync": {
|
||||
"enabled": state_sync is not None,
|
||||
"healthy": bool(getattr(state_sync, "healthy", True)),
|
||||
"paused": state_sync_paused,
|
||||
"generation": int(getattr(state_sync, "generation", 0) or 0),
|
||||
},
|
||||
}
|
||||
write_json(run_dir / "summary.json", summary)
|
||||
return summary
|
||||
|
||||
Reference in New Issue
Block a user