From f0197bfa83d10e26ff863b560721b313b50993c5 Mon Sep 17 00:00:00 2001 From: Dobromir Popov Date: Fri, 17 Jul 2026 14:29:09 +0300 Subject: [PATCH] fix: reconcile merged runtime behavior --- packages/node/meshnet_node/doctor.py | 50 ++++++---------------- packages/node/meshnet_node/torch_server.py | 38 +++++++++------- packages/tracker/meshnet_tracker/server.py | 15 ++++--- 3 files changed, 45 insertions(+), 58 deletions(-) diff --git a/packages/node/meshnet_node/doctor.py b/packages/node/meshnet_node/doctor.py index 6e25a4f..5ceff88 100644 --- a/packages/node/meshnet_node/doctor.py +++ b/packages/node/meshnet_node/doctor.py @@ -36,7 +36,6 @@ from .capability import ( CapabilityReport, build_capability_report, ) -from .native_backend import NativeWorkerBackendAdapter from .recipe_manifest import ( DEFAULT_RECIPE_ID, Recipe, @@ -450,9 +449,11 @@ def _validate_recipe( category: str | None = None error: BaseException | None = None diagnostics: list[str] = [] + detail: dict = {} + try: backend = load_backend(selection, recipe) - probe_forward(backend) + detail = probe_forward(backend) except DoctorError as exc: category, error = exc.category, exc diagnostics = [str(exc), exc.hint] @@ -463,48 +464,23 @@ def _validate_recipe( duration_ms = int((time.monotonic() - started) * 1000) device = _backend_device(backend, selection) - # Only the native adapter has an authoritative immutable GGUF report and - # deployment pin. The Transformers path deliberately remains dark: a - # model/config fingerprint is not an exact ArtifactIdentity. - identity = backend.identity if isinstance(backend, NativeWorkerBackendAdapter) else None - model_id = selection.model_id if identity is None else identity.artifact.artifact_id - shard_start = selection.shard_start if identity is None else identity.shard_start - shard_end = selection.shard_end if identity is None else identity.shard_end - 1 - recipe_id = recipe.id if identity is None else identity.recipe.recipe_id - recipe_version = recipe.version if identity is None else identity.recipe.recipe_version - catalogue_version = ( - manifest.catalogue_version if identity is None else identity.recipe.catalogue_version - ) - backend_id = recipe.backend_id if identity is None else identity.recipe.backend_id - quantization = ( - selection.quantization if identity is None else identity.recipe.weight_quantization - ) - runtime = _runtime_versions() - model_config = _model_config(backend) - revision = None - if identity is not None: - revision = identity.artifact.revision - model_config = "sha256:" + identity.artifact.architecture_digest - runtime = {**runtime, "native_runtime": identity.recipe.runtime_version} report = build_capability_report( - model_id=model_id, - shard_start=shard_start, - shard_end=shard_end, - recipe_id=recipe_id, - recipe_version=recipe_version, - catalogue_version=catalogue_version, - backend_id=backend_id, + model_id=selection.model_id, + shard_start=selection.shard_start, + shard_end=selection.shard_end, + recipe_id=recipe.id, + recipe_version=recipe.version, + catalogue_version=manifest.catalogue_version, + backend_id=recipe.backend_id, device=device, device_name=_backend_device_name(device), - quantization=quantization, - runtime=runtime, - revision=revision, - model_config=model_config, + quantization=selection.quantization, + runtime=_runtime_versions(), + model_config=_model_config(backend), status=STATUS_FAILED if category else STATUS_PASSED, duration_ms=duration_ms, diagnostics=[d for d in diagnostics if d] or None, validated_at=clock(), - identity=identity, ) if category: return RecipeResult( diff --git a/packages/node/meshnet_node/torch_server.py b/packages/node/meshnet_node/torch_server.py index d464561..fb1e74e 100644 --- a/packages/node/meshnet_node/torch_server.py +++ b/packages/node/meshnet_node/torch_server.py @@ -1576,19 +1576,24 @@ class TorchNodeServer: if drop_directive is not None: model_id = str(drop_directive.get("model") or "") removed = self._backends.pop(model_id, None) - if removed is None: - return None - if self._backends: - self._backend = next(iter(self._backends.values())) - self._tracker_mode = self._backend.shard_start == 0 - else: - self._backend = None - self._tracker_mode = False - if self._server is not None: - self._server.backends = dict(self._backends) - self._server.backend = self._backend - self._server.tracker_mode = self._tracker_mode - return {"action": "DROP_SHARD", "model": model_id} + if removed is not None: + if self._backends: + self._backend = next(iter(self._backends.values())) + self._tracker_mode = self._backend.shard_start == 0 + else: + self._backend = None + self._tracker_mode = False + if self._server is not None: + self._server.backends = dict(self._backends) + self._server.backend = self._backend + self._server.tracker_mode = self._tracker_mode + # A DROP_SHARD may be paired with a subsequent LOAD_SHARD in the + # same heartbeat; continue so the replacement is applied atomically. + if not any( + directive.get("action") in {"ADD_SHARD", "LOAD_SHARD"} + for directive in directives + ): + return {"action": "DROP_SHARD", "model": model_id} if removed is not None else None add_directive = next( (directive for directive in reversed(directives) if directive.get("action") == "ADD_SHARD"), None, @@ -1602,8 +1607,11 @@ class TorchNodeServer: return None shard_start = int(directive["shard_start"]) shard_end = int(directive["shard_end"]) - quantization = str(directive.get("quantization") or self._backend.quantization) - model_id = str(directive.get("model") or self._backend.model_id) + quantization = str( + directive.get("quantization") + or (self._backend.quantization if self._backend is not None else "auto") + ) + model_id = str(directive.get("model") or (self._backend.model_id if self._backend is not None else "")) replacing = directive.get("action") == "LOAD_SHARD" if not replacing and len(self._backends) >= self._max_loaded_shards: print( diff --git a/packages/tracker/meshnet_tracker/server.py b/packages/tracker/meshnet_tracker/server.py index 43e50ce..40c2290 100644 --- a/packages/tracker/meshnet_tracker/server.py +++ b/packages/tracker/meshnet_tracker/server.py @@ -3295,12 +3295,15 @@ class _TrackerHandler(http.server.BaseHTTPRequestHandler): node.hf_repo or node.model for node in alive if node.model is not None - # The same model can be registered under its HF repository while - # the catalogue exposes its short preset id. Do not emit a second - # repo-keyed entry when either node identifier resolves to a preset. - and _resolve_model_preset( - server.model_presets, node.hf_repo or node.model, - )[1] is None + # Explicit HF repositories are emitted as stable identifiers even + # when they also resolve to a short-name preset; clients may use + # either identifier when selecting a model. + and ( + node.hf_repo is not None + or _resolve_model_preset( + server.model_presets, node.model, + )[1] is None + ) and node.shard_start is not None and node.shard_end is not None and node.num_layers is not None