@meridiona/meridian-darwin-arm64 1.66.1 → 1.67.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/VERSION CHANGED
@@ -1 +1 @@
1
- 1.66.1
1
+ 1.67.0
package/bin/meridian CHANGED
Binary file
package/bin/meridian-tray CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meridiona/meridian-darwin-arm64",
3
- "version": "1.66.1",
3
+ "version": "1.67.0",
4
4
  "description": "Prebuilt Meridian app for macOS arm64 (daemon binary + dashboard + Python services). Installed via @meridiona/meridian.",
5
5
  "homepage": "https://github.com/Meridiona/meridian",
6
6
  "repository": {
@@ -16,6 +16,7 @@ from __future__ import annotations
16
16
 
17
17
  import asyncio
18
18
  import logging
19
+ import os
19
20
  import threading
20
21
  import time
21
22
  from pathlib import Path
@@ -31,10 +32,11 @@ log = logging.getLogger("agents.server")
31
32
  router = APIRouter()
32
33
 
33
34
  # Speed is derived from the growth of on-disk `received` between /prefetch_status
34
- # polls — NOT from a tqdm hook. hf_xet (the Xet accelerator these models use) does
35
- # its download in Rust and bypasses the classic tqdm bar, so a tqdm_class never
36
- # fires; the byte-delta approach works for any backend. Stored as a dict so both
37
- # functions can mutate it in-place without `global` declarations.
35
+ # polls — NOT from a tqdm hook (a tqdm_class override never fires reliably across
36
+ # backends). The byte-delta approach is backend-agnostic; with Xet disabled for the
37
+ # prefetch (see `_download_spec`) the classic path grows the blob linearly, so this
38
+ # now reads as a smooth rate rather than the 0-then-burst pattern hf_xet produced.
39
+ # Stored as a dict so both functions can mutate it in-place without `global`s.
38
40
  _speed_state: dict[str, float | int] = {"recv": 0, "ts": 0.0}
39
41
 
40
42
 
@@ -81,17 +83,93 @@ def _spec_total_bytes(spec: model_registry.ModelSpec) -> int:
81
83
  return total
82
84
 
83
85
 
86
+ def _env_positive_int(name: str, default: int) -> int:
87
+ """Read a positive-int env override, falling back to `default` on bad input.
88
+
89
+ Parsed at import, so a non-integer value (operator typo) must degrade to the
90
+ default rather than raise ValueError and brick MLX-server startup. Non-positive
91
+ integers are also rejected for consistency.
92
+ """
93
+ raw = os.environ.get(name)
94
+ if raw is None:
95
+ return default
96
+ try:
97
+ val = int(raw)
98
+ if val <= 0:
99
+ log.warning(
100
+ "server: ignoring non-positive env override; using default",
101
+ extra={"env_var": name, "raw": raw, "default": default},
102
+ )
103
+ return default
104
+ return val
105
+ except ValueError:
106
+ log.warning(
107
+ "server: ignoring non-integer env override; using default",
108
+ extra={"env_var": name, "raw": raw, "default": default},
109
+ )
110
+ return default
111
+
112
+
113
+ # How many times to (re)enter snapshot_download for one model before giving up.
114
+ # Each retry resumes from the on-disk ``.incomplete`` partial (HF Range request),
115
+ # so bytes already pulled are never re-downloaded. Env-overridable for ops.
116
+ _PREFETCH_MAX_ATTEMPTS = _env_positive_int("MERIDIAN_PREFETCH_MAX_ATTEMPTS", 5)
117
+
118
+
84
119
  def _download_spec(spec: model_registry.ModelSpec) -> None:
85
120
  """Download one model's weights to the HF cache (no load into memory).
86
121
 
87
- All specs go through ``snapshot_download`` (filtered to the spec's
88
- allow_patterns); ``hf-xet`` transparently accelerates the Xet-backed transfers.
122
+ Forces the classic (non-Xet) transfer path and retries with resume so a
123
+ stalled connection can never wedge the download indefinitely:
124
+
125
+ * **Xet is disabled for the pull.** ``hf_xet`` does its transfer in Rust with
126
+ no per-read timeout and can't be interrupted from Python, so a half-open
127
+ connection to the Xet CAS endpoint hangs forever (observed: a prefetch stuck
128
+ at 57% for 4h on a healthy 16 Mbps link — bytes never resumed). The classic
129
+ path caps every read at ``HF_HUB_DOWNLOAD_TIMEOUT`` (10s) and resumes from the
130
+ ``.incomplete`` partial via a Range request, so a stall raises and self-heals
131
+ instead of hanging. On a cold first-run pull of distinct model weights Xet's
132
+ chunk-dedup buys little, so this trades an unused fast-path for a bounded,
133
+ resumable one. (Bonus: the classic path grows the on-disk blob linearly, so
134
+ ``/prefetch_status``'s byte-delta speed reads smoothly instead of sitting at 0
135
+ then jumping in bursts as hf_xet flushes reconstructed chunks.)
136
+ * **Bounded retries** re-enter ``snapshot_download`` on any error; each attempt
137
+ resumes from the partial, so transient drops that exhaust the library's own
138
+ per-read retries still converge without re-pulling what's on disk.
139
+
89
140
  ``mlx_lm.load`` / ``mlx_embeddings.load`` resolve the files straight from the
90
141
  cache afterwards. Only disk is touched — the single-slot runtime loads each
91
- model lazily on first use.
142
+ model lazily on first use. Disabling Xet here is process-global (it flips a
143
+ huggingface_hub module constant), so later in-process model loads inherit the
144
+ same bounded, resumable path — which is what we want on a network where Xet
145
+ stalls.
92
146
  """
147
+ from huggingface_hub import constants as hf_constants
93
148
  from huggingface_hub import snapshot_download
94
- snapshot_download(spec.model_id, allow_patterns=spec.allow_patterns)
149
+
150
+ # Flip the *module attribute* (re-read per download at file_download.py call
151
+ # sites), NOT the env var: the env is parsed into this constant once at import,
152
+ # so ``os.environ`` here would be a no-op for an already-imported library.
153
+ hf_constants.HF_HUB_DISABLE_XET = True
154
+
155
+ for attempt in range(1, _PREFETCH_MAX_ATTEMPTS + 1):
156
+ try:
157
+ snapshot_download(spec.model_id, allow_patterns=spec.allow_patterns)
158
+ except Exception as exc: # noqa: BLE001 — retry-with-resume, re-raise if exhausted
159
+ log.warning(
160
+ "server: prefetch download attempt failed; will resume from partial",
161
+ extra={
162
+ "model_id": spec.model_id,
163
+ "attempt": attempt,
164
+ "max_attempts": _PREFETCH_MAX_ATTEMPTS,
165
+ "error": str(exc),
166
+ },
167
+ )
168
+ if attempt >= _PREFETCH_MAX_ATTEMPTS:
169
+ raise # all attempts exhausted — surface the active error to _run_prefetch
170
+ time.sleep(min(2**attempt, 30)) # 2s, 4s, 8s, 16s … capped at 30s
171
+ else:
172
+ return # download succeeded
95
173
 
96
174
 
97
175
  def _run_prefetch(specs: list[model_registry.ModelSpec]) -> None:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "meridian-agents"
7
- version = "1.66.1"
7
+ version = "1.67.0"
8
8
  description = "Meridian agents — MLX classifier server and Jira worklog synthesis for meridian.db"
9
9
  requires-python = ">=3.11"
10
10
  authors = [{ name = "Meridiona" }]