hugpy-wrapper 0.1.0__py3-none-any.whl
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.
- fitevict/__init__.py +34 -0
- fitevict/adapters/__init__.py +6 -0
- fitevict/adapters/db.py +487 -0
- fitevict/adapters/llama_cpp.py +1298 -0
- fitevict/adapters/transformers.py +149 -0
- fitevict/calls_view.py +225 -0
- fitevict/evict.py +167 -0
- fitevict/flex.py +174 -0
- fitevict/front_door.py +782 -0
- fitevict/gpu_state.py +173 -0
- fitevict/host.py +84 -0
- fitevict/lifted/__init__.py +8 -0
- fitevict/lifted/app_dirs.py +177 -0
- fitevict/lifted/binaries.py +47 -0
- fitevict/lifted/constants.py +91 -0
- fitevict/lifted/gguf_election.py +263 -0
- fitevict/lifted/gguf_inspect.py +985 -0
- fitevict/lifted/gguf_need.py +142 -0
- fitevict/lifted/hardware.py +204 -0
- fitevict/lifted/model_resolve.py +227 -0
- fitevict/lifted/native_resolve.py +115 -0
- fitevict/lifted/no_think.py +30 -0
- fitevict/lifted/nvml.py +128 -0
- fitevict/lifted/platform_facade.py +64 -0
- fitevict/lifted/procutil.py +78 -0
- fitevict/lifted/spill_reserve.py +227 -0
- fitevict/lifted/tf_child.py +556 -0
- fitevict/lifted/tf_facts.py +144 -0
- fitevict/lifted/timings.py +148 -0
- fitevict/plan.py +312 -0
- fitevict/predict.py +107 -0
- fitevict/run_engine.py +223 -0
- fitevict/sizing.py +585 -0
- fitevict/types.py +345 -0
- hugpy_wrapper-0.1.0.dist-info/METADATA +101 -0
- hugpy_wrapper-0.1.0.dist-info/RECORD +39 -0
- hugpy_wrapper-0.1.0.dist-info/WHEEL +5 -0
- hugpy_wrapper-0.1.0.dist-info/entry_points.txt +3 -0
- hugpy_wrapper-0.1.0.dist-info/top_level.txt +1 -0
fitevict/__init__.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""fitevict — a pure, deterministic evict-to-fit / placement allocator.
|
|
2
|
+
|
|
3
|
+
Clean-room: NO hugpy import. The core (`types`, `evict`, `plan`) is stdlib-only
|
|
4
|
+
and reads nothing live — every fact arrives in a frozen record captured once by
|
|
5
|
+
the caller, so identical records produce byte-identical plans. The `host` seam
|
|
6
|
+
(`EngineHost` / `drive`) and the `adapters` package hold the impure edges that
|
|
7
|
+
wrap a real inference engine (llama.cpp first).
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from .evict import evict_order, plan_eviction, sort_key
|
|
12
|
+
from .host import DriveResult, EngineHost, drive
|
|
13
|
+
from .plan import plan_fit
|
|
14
|
+
from .types import (
|
|
15
|
+
ACTIONS, FAILURE_KINDS, RAM, VRAM, DeviceBudget, Eviction, EvictionPlan,
|
|
16
|
+
FitFailure, FitPlan, HostBudget, LoadRequest, ModelFootprint, Offload,
|
|
17
|
+
Policy, Resident, canonical_key, key_equivalent,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
__version__ = "0.1.0"
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"__version__",
|
|
24
|
+
# types
|
|
25
|
+
"VRAM", "RAM", "ACTIONS", "FAILURE_KINDS", "canonical_key", "key_equivalent",
|
|
26
|
+
"DeviceBudget", "HostBudget", "ModelFootprint", "Resident", "LoadRequest",
|
|
27
|
+
"Policy", "Eviction", "EvictionPlan", "FitFailure", "Offload", "FitPlan",
|
|
28
|
+
# evict
|
|
29
|
+
"sort_key", "plan_eviction", "evict_order",
|
|
30
|
+
# plan
|
|
31
|
+
"plan_fit",
|
|
32
|
+
# host
|
|
33
|
+
"EngineHost", "DriveResult", "drive",
|
|
34
|
+
]
|
fitevict/adapters/db.py
ADDED
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
"""Call-log writer for the fitevict engine — Postgres `inference_engine.call_log`.
|
|
2
|
+
|
|
3
|
+
GLUE (new code, not lifted): one row per call, UPSERT by request_id, fail-open.
|
|
4
|
+
Stores RAW values only; derived values come from `call_log_derived`. Timestamps
|
|
5
|
+
are passed as epoch floats and written to `timestamptz` via to_timestamp().
|
|
6
|
+
|
|
7
|
+
Connects as the `hugpy` OS user the way the task specifies — psql is owned by
|
|
8
|
+
hugpy, so the engine shells UPSERTs through `sudo -u hugpy psql -d inference_engine`
|
|
9
|
+
when it cannot open a direct psycopg connection. A direct psycopg connection is
|
|
10
|
+
tried first (dsn from HUGPY_CALLLOG_DSN) and used when available.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
import subprocess
|
|
16
|
+
from typing import Any, Dict, Optional
|
|
17
|
+
|
|
18
|
+
_COLS = (
|
|
19
|
+
"request_id", "model_name", "received_at", "dispatched_at",
|
|
20
|
+
"weights_staged_at", "weights_loaded_at", "inference_started_at",
|
|
21
|
+
"inference_ended_at", "responded_at", "worker", "context_length",
|
|
22
|
+
"total_layers", "n_gpu_layers", "n_cpu_moe", "kv_cache_bytes",
|
|
23
|
+
"non_expert_bytes", "expert_bytes", "quantization", "is_moe",
|
|
24
|
+
"load_in_4bit", "alloc_mode", "prefer_gpu", "prefer_ram",
|
|
25
|
+
"prompt_tokens", "completion_tokens", "decode_tokens", "task",
|
|
26
|
+
"caller", "callee", "request_params", "responder", "recipient",
|
|
27
|
+
"response_meta", "from_ip", "to_ip", "endpoint", "model_path",
|
|
28
|
+
"mmproj_path", "model_bytes", "llama_port", "llama_argv", "forwarded_at",
|
|
29
|
+
"request_body", "forwarded_body", "response_body", "plan_verdict",
|
|
30
|
+
"plan_note", "predicted_need_bytes", "free_vram_before",
|
|
31
|
+
"free_vram_after_load", "measured_vram_after_load",
|
|
32
|
+
"bytes_freed_by_eviction", "evicted_keys", "http_status", "error",
|
|
33
|
+
"layers_ram", "gpu_slices", "attention", "process_bytes_after_call",
|
|
34
|
+
)
|
|
35
|
+
_TS_COLS = {"received_at", "dispatched_at", "weights_staged_at",
|
|
36
|
+
"weights_loaded_at", "inference_started_at", "inference_ended_at",
|
|
37
|
+
"responded_at", "forwarded_at"}
|
|
38
|
+
_JSON_COLS = {"request_params", "response_meta", "request_body",
|
|
39
|
+
"forwarded_body", "response_body", "gpu_slices", "attention"}
|
|
40
|
+
_ARRAY_COLS = {"llama_argv", "evicted_keys"}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _text(v: Any) -> str:
|
|
44
|
+
return "'" + str(v).replace("'", "''") + "'"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _sql_literal(col: str, val: Any) -> str:
|
|
48
|
+
if val is None:
|
|
49
|
+
return "NULL"
|
|
50
|
+
if col in _TS_COLS:
|
|
51
|
+
return f"to_timestamp({float(val)})"
|
|
52
|
+
if col in _JSON_COLS:
|
|
53
|
+
js = json.dumps(val, default=str)
|
|
54
|
+
return "'" + js.replace("'", "''") + "'::jsonb"
|
|
55
|
+
if col in _ARRAY_COLS:
|
|
56
|
+
return "ARRAY[" + ", ".join(_text(v) for v in val) + "]::text[]"
|
|
57
|
+
if isinstance(val, bool):
|
|
58
|
+
return "TRUE" if val else "FALSE"
|
|
59
|
+
if isinstance(val, (int, float)):
|
|
60
|
+
return repr(val)
|
|
61
|
+
return _text(val)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def upsert(row: Dict[str, Any]) -> Optional[int]:
|
|
65
|
+
"""UPSERT one call_log row by request_id. Returns the row id, or None on a
|
|
66
|
+
failure that was swallowed fail-open (the call still happened)."""
|
|
67
|
+
cols = [c for c in _COLS if c in row]
|
|
68
|
+
vals = [_sql_literal(c, row.get(c)) for c in cols]
|
|
69
|
+
set_clause = ", ".join(f"{c}=EXCLUDED.{c}" for c in cols if c != "request_id")
|
|
70
|
+
sql = (
|
|
71
|
+
f"INSERT INTO call_log ({', '.join(cols)}) VALUES ({', '.join(vals)}) "
|
|
72
|
+
f"ON CONFLICT (request_id) DO UPDATE SET {set_clause} RETURNING id;"
|
|
73
|
+
)
|
|
74
|
+
try:
|
|
75
|
+
out = subprocess.run(
|
|
76
|
+
["sudo", "-u", "hugpy", "psql", "-d", "inference_engine", "-tA",
|
|
77
|
+
"-c", sql],
|
|
78
|
+
capture_output=True, text=True, timeout=30)
|
|
79
|
+
if out.returncode != 0:
|
|
80
|
+
raise RuntimeError(out.stderr.strip() or "psql nonzero exit")
|
|
81
|
+
txt = out.stdout.strip().splitlines()
|
|
82
|
+
return int(txt[0]) if txt and txt[0].strip().isdigit() else None
|
|
83
|
+
except Exception as exc: # noqa: BLE001 — fail-open; never lose the call
|
|
84
|
+
print(f"[call_log] UPSERT failed (fail-open): {exc}")
|
|
85
|
+
return None
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
# ── residents: the box's live llama-server seats (written at every edge) ──────
|
|
89
|
+
_RES_COLS = (
|
|
90
|
+
("key", "text"), ("pid", "int"), ("port", "int"), ("state", "text"),
|
|
91
|
+
("alive", "boolean"), ("healthy", "boolean"), ("busy", "boolean"),
|
|
92
|
+
("loading", "boolean"), ("load_error", "text"), ("vram_bytes", "bigint"),
|
|
93
|
+
("ram_bytes", "bigint"), ("device", "int"), ("n_gpu_layers", "int"),
|
|
94
|
+
("total_layers", "int"), ("n_cpu_moe", "int"), ("ctx", "int"),
|
|
95
|
+
("is_moe", "boolean"), ("quant", "text"), ("model_file", "text"),
|
|
96
|
+
("endpoint", "text"), ("resident_since", "float8"),
|
|
97
|
+
("weights_loaded_at", "float8"), ("last_used", "float8"), ("calls", "int"),
|
|
98
|
+
("engine", "text"),
|
|
99
|
+
)
|
|
100
|
+
_RES_TS = {"resident_since", "weights_loaded_at", "last_used"}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def write_residents(worker: str, rows: "list[dict]") -> bool:
|
|
104
|
+
"""Replace this box's residents with ``rows`` in one statement: upsert every
|
|
105
|
+
seat, delete the seats no longer present (evicted). Fail-open."""
|
|
106
|
+
snap = [{c: r.get(c) for c, _ in _RES_COLS} for r in rows]
|
|
107
|
+
js = json.dumps(snap, default=str).replace("'", "''")
|
|
108
|
+
w = _text(worker)
|
|
109
|
+
rec = ", ".join(f"{c} {t}" for c, t in _RES_COLS)
|
|
110
|
+
sel = ", ".join((f"to_timestamp(s.{c})" if c in _RES_TS else f"s.{c}")
|
|
111
|
+
for c, _ in _RES_COLS)
|
|
112
|
+
cols = ", ".join(c for c, _ in _RES_COLS)
|
|
113
|
+
upd = ", ".join(f"{c}=EXCLUDED.{c}" for c, _ in _RES_COLS if c != "key")
|
|
114
|
+
sql = (
|
|
115
|
+
f"WITH s AS (SELECT * FROM jsonb_to_recordset('{js}'::jsonb) AS x({rec})), "
|
|
116
|
+
f"gone AS (DELETE FROM residents WHERE worker={w} "
|
|
117
|
+
f"AND key NOT IN (SELECT key FROM s)) "
|
|
118
|
+
f"INSERT INTO residents (worker, {cols}, observed_at) "
|
|
119
|
+
f"SELECT {w}, {sel}, now() FROM s "
|
|
120
|
+
f"ON CONFLICT (worker, key) DO UPDATE SET {upd}, observed_at=now();"
|
|
121
|
+
)
|
|
122
|
+
try:
|
|
123
|
+
out = subprocess.run(
|
|
124
|
+
["sudo", "-u", "hugpy", "psql", "-d", "inference_engine", "-tA",
|
|
125
|
+
"-c", sql],
|
|
126
|
+
capture_output=True, text=True, timeout=10)
|
|
127
|
+
if out.returncode != 0:
|
|
128
|
+
raise RuntimeError(out.stderr.strip() or "psql nonzero exit")
|
|
129
|
+
return True
|
|
130
|
+
except Exception as exc: # noqa: BLE001 — fail-open; serving never waits on this
|
|
131
|
+
print(f"[residents] write failed (fail-open): {exc}")
|
|
132
|
+
return False
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
# ── models: the box's GGUF catalog (the wrapper's discovery is the source) ───
|
|
137
|
+
def write_models(worker: str, rows: "list[dict]") -> bool:
|
|
138
|
+
"""Replace this box's catalog with ``rows`` ({key, path, model_dir, quant,
|
|
139
|
+
bytes, files}) in one statement; keys gone from disk are deleted. Fail-open."""
|
|
140
|
+
js = json.dumps(rows, default=str).replace("'", "''")
|
|
141
|
+
w = _text(worker)
|
|
142
|
+
sql = (
|
|
143
|
+
f"WITH s AS (SELECT * FROM jsonb_to_recordset('{js}'::jsonb) AS x("
|
|
144
|
+
"key text, path text, model_dir text, quant text, bytes bigint, files jsonb, engine text)), "
|
|
145
|
+
f"gone AS (DELETE FROM models WHERE worker={w} AND key NOT IN (SELECT key FROM s)) "
|
|
146
|
+
"INSERT INTO models (worker, key, path, model_dir, quant, bytes, files, engine, seen_at) "
|
|
147
|
+
f"SELECT {w}, key, path, model_dir, quant, bytes, files, COALESCE(engine, 'gguf'), now() FROM s "
|
|
148
|
+
"ON CONFLICT (worker, key) DO UPDATE SET path=EXCLUDED.path, engine=EXCLUDED.engine, "
|
|
149
|
+
"model_dir=EXCLUDED.model_dir, quant=EXCLUDED.quant, bytes=EXCLUDED.bytes, "
|
|
150
|
+
"files=EXCLUDED.files, seen_at=now();"
|
|
151
|
+
)
|
|
152
|
+
try:
|
|
153
|
+
out = subprocess.run(
|
|
154
|
+
["sudo", "-u", "hugpy", "psql", "-d", "inference_engine", "-tA", "-c", sql],
|
|
155
|
+
capture_output=True, text=True, timeout=15)
|
|
156
|
+
if out.returncode != 0:
|
|
157
|
+
raise RuntimeError(out.stderr.strip() or "psql nonzero exit")
|
|
158
|
+
return True
|
|
159
|
+
except Exception as exc: # noqa: BLE001
|
|
160
|
+
print(f"[models] catalog write failed (fail-open): {exc}")
|
|
161
|
+
return False
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
# ── the pair's knobs (model_workers.user_settings) — the DB is the contract ──
|
|
165
|
+
def pair_knobs(worker: str, key: str) -> dict:
|
|
166
|
+
"""The (worker, model) pair's operator knobs from the hugpy DB; {} when the
|
|
167
|
+
pair has none or the DB is unreachable (the call then runs at defaults)."""
|
|
168
|
+
w, k = _text(worker), _text(key)
|
|
169
|
+
sql = (
|
|
170
|
+
"SELECT coalesce(mw.user_settings, '{}'::jsonb)::text FROM model_workers mw "
|
|
171
|
+
"JOIN models m ON m.id = mw.model_id "
|
|
172
|
+
"WHERE mw.worker_id = (SELECT worker_id FROM hugpy_worker_heartbeat "
|
|
173
|
+
f"WHERE name = {w} ORDER BY ts DESC LIMIT 1) "
|
|
174
|
+
f"AND (m.name = {k} OR m.name LIKE '%~' || {k} OR {k} LIKE '%~' || m.name) "
|
|
175
|
+
"ORDER BY (m.name = " + k + ") DESC LIMIT 1;"
|
|
176
|
+
)
|
|
177
|
+
try:
|
|
178
|
+
out = subprocess.run(["sudo", "-u", "hugpy", "psql", "-d", "hugpy", "-tA", "-c", sql],
|
|
179
|
+
capture_output=True, text=True, timeout=10)
|
|
180
|
+
if out.returncode != 0:
|
|
181
|
+
raise RuntimeError(out.stderr.strip() or "psql nonzero exit")
|
|
182
|
+
txt = out.stdout.strip()
|
|
183
|
+
return json.loads(txt) if txt else {}
|
|
184
|
+
except Exception as exc: # noqa: BLE001
|
|
185
|
+
print(f"[knobs] read failed for {key}: {exc}")
|
|
186
|
+
return {}
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def static_keys(worker: str) -> "Optional[set]":
|
|
190
|
+
"""The worker's 🔒static models from the DB pair rows
|
|
191
|
+
(model_workers.user_settings.residency = 'static'). Static = never evicted
|
|
192
|
+
once loaded (operator 2026-10-03). None when the DB is unreachable."""
|
|
193
|
+
w = _text(worker)
|
|
194
|
+
sql = (
|
|
195
|
+
"SELECT coalesce(json_agg(m.name), '[]')::text FROM model_workers mw "
|
|
196
|
+
"JOIN models m ON m.id = mw.model_id "
|
|
197
|
+
"WHERE mw.worker_id = (SELECT worker_id FROM hugpy_worker_heartbeat "
|
|
198
|
+
f"WHERE name = {w} ORDER BY ts DESC LIMIT 1) "
|
|
199
|
+
"AND mw.user_settings->>'residency' = 'static';"
|
|
200
|
+
)
|
|
201
|
+
try:
|
|
202
|
+
txt = _psql_hugpy(sql)
|
|
203
|
+
return set(json.loads(txt or "[]"))
|
|
204
|
+
except Exception as exc: # noqa: BLE001
|
|
205
|
+
print(f"[static] read failed: {exc}")
|
|
206
|
+
return None
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _psql_hugpy(sql: str) -> str:
|
|
210
|
+
out = subprocess.run(["sudo", "-u", "hugpy", "psql", "-d", "hugpy", "-tA", "-c", sql],
|
|
211
|
+
capture_output=True, text=True, timeout=15)
|
|
212
|
+
if out.returncode != 0:
|
|
213
|
+
raise RuntimeError(out.stderr.strip() or "psql nonzero exit")
|
|
214
|
+
return out.stdout.strip()
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
# ── exact sizing: llama.cpp projections + measured CUDA context + transformers loads ──
|
|
218
|
+
def _ie(sql: str) -> str:
|
|
219
|
+
out = subprocess.run(["sudo", "-u", "hugpy", "psql", "-d", "inference_engine", "-tA", "-c", sql],
|
|
220
|
+
capture_output=True, text=True, timeout=15)
|
|
221
|
+
if out.returncode != 0:
|
|
222
|
+
raise RuntimeError(out.stderr.strip() or "psql nonzero exit")
|
|
223
|
+
return out.stdout.strip()
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _lit(v):
|
|
227
|
+
return "NULL" if v is None else (repr(int(v)) if isinstance(v, (int, float)) and not isinstance(v, bool) else _text(v))
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def get_projection(worker, file, ctx, ngl, ncm, kv):
|
|
231
|
+
try:
|
|
232
|
+
txt = _ie("SELECT row_to_json(p)::text FROM gguf_projection p WHERE worker=%s AND file=%s AND ctx=%d "
|
|
233
|
+
"AND n_gpu_layers=%d AND n_cpu_moe=%d AND kv_type=%s"
|
|
234
|
+
% (_text(worker), _text(file), int(ctx), int(ngl), int(ncm), _text(kv)))
|
|
235
|
+
return json.loads(txt) if txt else None
|
|
236
|
+
except Exception as exc: # noqa: BLE001
|
|
237
|
+
print(f"[proj] read failed: {exc}")
|
|
238
|
+
return None
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def put_projection(worker, file, ctx, ngl, ncm, kv, p):
|
|
242
|
+
cols = ("dev_model", "dev_context", "dev_compute", "dev_self", "host_model", "host_context", "host_compute",
|
|
243
|
+
"host_repack")
|
|
244
|
+
try:
|
|
245
|
+
_ie("INSERT INTO gguf_projection (worker, file, ctx, n_gpu_layers, n_cpu_moe, kv_type, %s) VALUES (%s, %s, %d, %d, %d, %s, %s) "
|
|
246
|
+
"ON CONFLICT (worker, file, ctx, n_gpu_layers, n_cpu_moe, kv_type) DO UPDATE SET %s, at=now()"
|
|
247
|
+
% (", ".join(cols), _text(worker), _text(file), int(ctx), int(ngl), int(ncm), _text(kv),
|
|
248
|
+
", ".join(_lit(p.get(c)) for c in cols), ", ".join(f"{c}=EXCLUDED.{c}" for c in cols)))
|
|
249
|
+
except Exception as exc: # noqa: BLE001
|
|
250
|
+
print(f"[proj] write failed: {exc}")
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def get_cuda_context(worker, device):
|
|
254
|
+
try:
|
|
255
|
+
txt = _ie("SELECT bytes FROM cuda_context WHERE worker=%s AND device=%s" % (_text(worker), _text(device)))
|
|
256
|
+
return int(txt) if txt else None
|
|
257
|
+
except Exception: # noqa: BLE001
|
|
258
|
+
return None
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def put_cuda_context(worker, device, nbytes):
|
|
262
|
+
try:
|
|
263
|
+
_ie("INSERT INTO cuda_context (worker, device, bytes) VALUES (%s, %s, %d) ON CONFLICT (worker, device) "
|
|
264
|
+
"DO UPDATE SET bytes=EXCLUDED.bytes, samples=cuda_context.samples+1, at=now()"
|
|
265
|
+
% (_text(worker), _text(device), int(nbytes)))
|
|
266
|
+
except Exception as exc: # noqa: BLE001
|
|
267
|
+
print(f"[cuda_ctx] write failed: {exc}")
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def get_tf_measure(worker, key, bnb4, ctx):
|
|
271
|
+
try:
|
|
272
|
+
where = "" if ctx is None else " AND ctx=%d" % int(ctx)
|
|
273
|
+
txt = _ie("SELECT row_to_json(t)::text FROM tf_measure t WHERE worker=%s AND key=%s AND bnb4=%s%s "
|
|
274
|
+
"ORDER BY at DESC LIMIT 1" % (_text(worker), _text(key), "true" if bnb4 else "false", where))
|
|
275
|
+
return json.loads(txt) if txt else None
|
|
276
|
+
except Exception: # noqa: BLE001
|
|
277
|
+
return None
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def put_tf_measure(worker, key, bnb4, ctx, tensor_bytes=None, load_bytes=None, peak_bytes=None):
|
|
281
|
+
try:
|
|
282
|
+
_ie("INSERT INTO tf_measure (worker, key, bnb4, ctx, tensor_bytes, load_bytes, peak_bytes) VALUES (%s, %s, %s, %d, %s, %s, %s) "
|
|
283
|
+
"ON CONFLICT (worker, key, bnb4, ctx) DO UPDATE SET "
|
|
284
|
+
"tensor_bytes=COALESCE(EXCLUDED.tensor_bytes, tf_measure.tensor_bytes), "
|
|
285
|
+
"load_bytes=COALESCE(EXCLUDED.load_bytes, tf_measure.load_bytes), "
|
|
286
|
+
"peak_bytes=GREATEST(COALESCE(EXCLUDED.peak_bytes, 0), COALESCE(tf_measure.peak_bytes, 0)), at=now()"
|
|
287
|
+
% (_text(worker), _text(key), "true" if bnb4 else "false", int(ctx),
|
|
288
|
+
_lit(tensor_bytes), _lit(load_bytes), _lit(peak_bytes)))
|
|
289
|
+
except Exception as exc: # noqa: BLE001
|
|
290
|
+
print(f"[tf_measure] write failed: {exc}")
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def put_measured(worker, file, ctx, ngl, ncm, kv, process_bytes, context_bytes):
|
|
294
|
+
"""G5/G3: the NVML-measured process bytes of a real load of this exact
|
|
295
|
+
configuration, and the context derived from its load-log buffers."""
|
|
296
|
+
try:
|
|
297
|
+
_ie("INSERT INTO gguf_projection (worker, file, ctx, n_gpu_layers, n_cpu_moe, kv_type, measured_process, measured_context, measured_at) "
|
|
298
|
+
"VALUES (%s, %s, %d, %d, %d, %s, %s, %s, now()) ON CONFLICT (worker, file, ctx, n_gpu_layers, n_cpu_moe, kv_type) "
|
|
299
|
+
"DO UPDATE SET measured_process=EXCLUDED.measured_process, measured_context=EXCLUDED.measured_context, measured_at=now()"
|
|
300
|
+
% (_text(worker), _text(file), int(ctx), int(ngl), int(ncm), _text(kv), _lit(process_bytes), _lit(context_bytes)))
|
|
301
|
+
except Exception as exc: # noqa: BLE001
|
|
302
|
+
print(f"[measured] write failed: {exc}")
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def put_eviction(row: dict) -> None:
|
|
306
|
+
"""One unload, timed and measured in bytes (inference_engine.evictions)."""
|
|
307
|
+
cols = ("worker", "key", "engine", "request_id", "reason", "pid", "process_bytes", "free_before", "free_after")
|
|
308
|
+
ts = ("started_at", "exited_at", "released_at")
|
|
309
|
+
vals = [_lit(row.get(c)) for c in cols] + [("to_timestamp(%r)" % float(row[t])) if row.get(t) else "NULL" for t in ts]
|
|
310
|
+
try:
|
|
311
|
+
_ie("INSERT INTO evictions (%s) VALUES (%s)" % (", ".join(cols + ts), ", ".join(vals)))
|
|
312
|
+
except Exception as exc: # noqa: BLE001
|
|
313
|
+
print(f"[eviction] write failed: {exc}")
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def get_full_ctx(worker, file, ctx, ngl, ncm, kv):
|
|
317
|
+
"""G8/A: the stored full-context measurement for this configuration (or None)."""
|
|
318
|
+
try:
|
|
319
|
+
txt = _ie("SELECT full_ctx_process FROM gguf_projection WHERE worker=%s AND file=%s AND ctx=%d "
|
|
320
|
+
"AND n_gpu_layers=%d AND n_cpu_moe=%d AND kv_type=%s"
|
|
321
|
+
% (_text(worker), _text(file), int(ctx), int(ngl), int(ncm), _text(kv)))
|
|
322
|
+
return int(txt) if txt else None
|
|
323
|
+
except Exception: # noqa: BLE001
|
|
324
|
+
return None
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def put_full_ctx(worker, file, ctx, ngl, ncm, kv, process_bytes, tokens, prefill_s, request_id):
|
|
328
|
+
"""G8/A: NVML process bytes after a prompt that fills the whole context."""
|
|
329
|
+
try:
|
|
330
|
+
_ie("INSERT INTO gguf_projection (worker, file, ctx, n_gpu_layers, n_cpu_moe, kv_type, full_ctx_process, "
|
|
331
|
+
"full_ctx_tokens, full_ctx_prefill_s, full_ctx_request_id, full_ctx_at) VALUES (%s, %s, %d, %d, %d, %s, %d, %d, %r, %s, now()) "
|
|
332
|
+
"ON CONFLICT (worker, file, ctx, n_gpu_layers, n_cpu_moe, kv_type) DO UPDATE SET "
|
|
333
|
+
"full_ctx_process=EXCLUDED.full_ctx_process, full_ctx_tokens=EXCLUDED.full_ctx_tokens, "
|
|
334
|
+
"full_ctx_prefill_s=EXCLUDED.full_ctx_prefill_s, full_ctx_request_id=EXCLUDED.full_ctx_request_id, full_ctx_at=now()"
|
|
335
|
+
% (_text(worker), _text(file), int(ctx), int(ngl), int(ncm), _text(kv), int(process_bytes),
|
|
336
|
+
int(tokens), float(prefill_s), _text(request_id)))
|
|
337
|
+
except Exception as exc: # noqa: BLE001
|
|
338
|
+
print(f"[g8] write failed: {exc}")
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def ctx_choice(key: str, file: str) -> dict:
|
|
342
|
+
"""The context facts for (model, quant file) from the hugpy DB: the per-tick
|
|
343
|
+
KV ladder (model_quant_facts.kv_cost.ticks) and the history default
|
|
344
|
+
(model_default_ctx: smallest tick covering the model's largest real use).
|
|
345
|
+
{} when unknown."""
|
|
346
|
+
import re as _re
|
|
347
|
+
logical = _re.sub(r"-\d{5}-of-\d{5}\.gguf$", ".gguf", file or "") # split GGUF: the DB keys the logical name
|
|
348
|
+
k, f = _text(key), "(%s, %s)" % (_text(file), _text(logical))
|
|
349
|
+
sql = ("SELECT json_build_object('ticks', fq.kv_cost->'ticks', 'default_ctx', d.default_ctx, "
|
|
350
|
+
"'default_tick', d.default_tick, 'need_tokens', d.need_tokens, 'basis', d.basis)::text "
|
|
351
|
+
"FROM model_quant_facts fq JOIN models m ON m.id = fq.model_id "
|
|
352
|
+
"LEFT JOIN model_default_ctx d ON d.model_id = fq.model_id AND d.file = fq.file "
|
|
353
|
+
f"WHERE fq.file IN {f} AND (m.name = {k} OR m.name LIKE '%~' || {k} OR {k} LIKE '%~' || m.name) "
|
|
354
|
+
f"ORDER BY (m.name = {k}) DESC LIMIT 1;")
|
|
355
|
+
try:
|
|
356
|
+
txt = _psql_hugpy(sql)
|
|
357
|
+
return json.loads(txt) if txt else {}
|
|
358
|
+
except Exception as exc: # noqa: BLE001
|
|
359
|
+
print(f"[ctx] read failed for {key}: {exc}")
|
|
360
|
+
return {}
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def serving_growth(worker, file, ctx, ngl, ncm, kv):
|
|
364
|
+
"""G8 for a configuration: (bytes, source). The configuration's own measured
|
|
365
|
+
growth (test-fire full-context fill, else the largest seen in real calls);
|
|
366
|
+
before any measurement of it, the LARGEST G8 measured on this card (the G3
|
|
367
|
+
precedent: the card's measured value, labelled as a pre-measurement bound)."""
|
|
368
|
+
try:
|
|
369
|
+
txt = _ie(
|
|
370
|
+
"SELECT json_build_object("
|
|
371
|
+
" 'own', (SELECT serving_growth FROM gguf_projection WHERE worker=%s AND file=%s AND ctx=%d "
|
|
372
|
+
" AND n_gpu_layers=%d AND n_cpu_moe=%d AND kv_type=%s),"
|
|
373
|
+
" 'observed', (SELECT max_growth_observed FROM serving_growth_observed WHERE worker=%s "
|
|
374
|
+
" AND regexp_replace(model_path, '.*/', '')=%s AND ctx=%d AND n_gpu_layers=%d AND n_cpu_moe=%d),"
|
|
375
|
+
" 'card', greatest((SELECT max(serving_growth) FROM gguf_projection WHERE worker=%s),"
|
|
376
|
+
" (SELECT max(max_growth_observed) FROM serving_growth_observed WHERE worker=%s)))::text"
|
|
377
|
+
% (_text(worker), _text(file), int(ctx), int(ngl), int(ncm), _text(kv),
|
|
378
|
+
_text(worker), _text(file), int(ctx), int(ngl), int(ncm), _text(worker), _text(worker)))
|
|
379
|
+
d = json.loads(txt) if txt else {}
|
|
380
|
+
except Exception as exc: # noqa: BLE001
|
|
381
|
+
print(f"[g8] read failed: {exc}")
|
|
382
|
+
return 0, "unmeasured"
|
|
383
|
+
own = max([v for v in (d.get("own"), d.get("observed")) if v is not None] or [None]) \
|
|
384
|
+
if (d.get("own") is not None or d.get("observed") is not None) else None
|
|
385
|
+
if own is not None:
|
|
386
|
+
return int(own), "measured (this configuration)"
|
|
387
|
+
if d.get("card") is not None:
|
|
388
|
+
return int(d["card"]), "card max (pre-measurement bound)"
|
|
389
|
+
return 0, "unmeasured"
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def put_kv_structure(worker, file, st: dict, slots, ctx, log_path):
|
|
393
|
+
try:
|
|
394
|
+
_ie("INSERT INTO kv_structure (worker, file, kv_type, k_cell_bytes, v_cell_bytes, kv_layers, kv_seqs, rs_bytes, "
|
|
395
|
+
"rs_cells, rs_layers, slots, source_ctx, source_log, at) VALUES (%s, %s, %s, %d, %d, %d, %s, %d, %d, %d, %s, %d, %s, now()) "
|
|
396
|
+
"ON CONFLICT (worker, file, kv_type) DO UPDATE SET k_cell_bytes=EXCLUDED.k_cell_bytes, v_cell_bytes=EXCLUDED.v_cell_bytes, "
|
|
397
|
+
"kv_layers=EXCLUDED.kv_layers, kv_seqs=EXCLUDED.kv_seqs, rs_bytes=EXCLUDED.rs_bytes, rs_cells=EXCLUDED.rs_cells, "
|
|
398
|
+
"rs_layers=EXCLUDED.rs_layers, slots=EXCLUDED.slots, source_ctx=EXCLUDED.source_ctx, source_log=EXCLUDED.source_log, at=now()"
|
|
399
|
+
% (_text(worker), _text(file), _text(st["kv_type"]), st["k_cell_bytes"], st["v_cell_bytes"], st["kv_layers"],
|
|
400
|
+
_text(st["kv_seqs"]), st["rs_bytes"], st["rs_cells"], st["rs_layers"], _lit(slots), int(ctx), _text(log_path)))
|
|
401
|
+
except Exception as exc: # noqa: BLE001
|
|
402
|
+
print(f"[kv_structure] write failed: {exc}")
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def get_kv_structure(worker, file, kv_type="f16"):
|
|
406
|
+
"""The measured f16 structure (other KV types derive exactly from it)."""
|
|
407
|
+
try:
|
|
408
|
+
txt = _ie("SELECT row_to_json(k)::text FROM kv_structure k WHERE worker=%s AND file=%s AND kv_type='f16'"
|
|
409
|
+
% (_text(worker), _text(file)))
|
|
410
|
+
return json.loads(txt) if txt else None
|
|
411
|
+
except Exception: # noqa: BLE001
|
|
412
|
+
return None
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def rewrite_ticks(file: str, st: dict) -> None:
|
|
416
|
+
"""Replace the quant's DB KV ticks (hugpy.model_quant_facts.kv_cost.ticks)
|
|
417
|
+
with K2 device bytes from the measured structure; keeps each tick's ctx.
|
|
418
|
+
q8_0 / q4_0 cells are 17/32 and 9/32 of f16 (34 and 18 bytes per 32
|
|
419
|
+
elements); the recurrent state is f32 in every KV type."""
|
|
420
|
+
from .. import sizing as _S
|
|
421
|
+
import re as _re
|
|
422
|
+
file = _re.sub(r"-\d{5}-of-\d{5}\.gguf$", ".gguf", file or "") # the DB keys split GGUFs by logical name
|
|
423
|
+
kv = st["k_cell_bytes"] + st["v_cell_bytes"]
|
|
424
|
+
rs = int(st.get("rs_bytes") or 0)
|
|
425
|
+
try:
|
|
426
|
+
cur = _psql_hugpy("SELECT kv_cost->'ticks' FROM model_quant_facts WHERE file = %s LIMIT 1" % _text(file))
|
|
427
|
+
ticks = json.loads(cur) if cur else None
|
|
428
|
+
if not ticks:
|
|
429
|
+
return
|
|
430
|
+
new = {}
|
|
431
|
+
for k, t in ticks.items():
|
|
432
|
+
c = int(t["ctx"])
|
|
433
|
+
rsb = _S.page(rs) if rs else 0
|
|
434
|
+
new[k] = {"ctx": c, "f16": _S.page(c * kv) + rsb,
|
|
435
|
+
"q8_0": _S.page(c * kv * 17 // 32) + rsb, "q4_0": _S.page(c * kv * 9 // 32) + rsb}
|
|
436
|
+
js = json.dumps(new).replace("'", "''")
|
|
437
|
+
_psql_hugpy("UPDATE model_quant_facts SET kv_cost = kv_cost || jsonb_build_object('ticks', '%s'::jsonb, "
|
|
438
|
+
"'basis', 'llama.cpp load log (K2, measured)', 'bytes_per_token', %d, 'fixed_bytes', %d) "
|
|
439
|
+
"WHERE file = %s" % (js, kv, rs, _text(file)))
|
|
440
|
+
except Exception as exc: # noqa: BLE001
|
|
441
|
+
print(f"[ticks] rewrite failed for {file}: {exc}")
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def put_measured_host(worker, file, ctx, ngl, ncm, kv, anon, filebytes):
|
|
445
|
+
"""R1: RssAnon / RssFile of a real RAM (or partial) load of this config."""
|
|
446
|
+
try:
|
|
447
|
+
_ie("INSERT INTO gguf_projection (worker, file, ctx, n_gpu_layers, n_cpu_moe, kv_type, measured_host_anon, "
|
|
448
|
+
"measured_host_file, measured_host_at) VALUES (%s, %s, %d, %d, %d, %s, %d, %d, now()) "
|
|
449
|
+
"ON CONFLICT (worker, file, ctx, n_gpu_layers, n_cpu_moe, kv_type) DO UPDATE SET "
|
|
450
|
+
"measured_host_anon=EXCLUDED.measured_host_anon, measured_host_file=EXCLUDED.measured_host_file, measured_host_at=now()"
|
|
451
|
+
% (_text(worker), _text(file), int(ctx), int(ngl), int(ncm), _text(kv), int(anon), int(filebytes)))
|
|
452
|
+
except Exception as exc: # noqa: BLE001
|
|
453
|
+
print(f"[host] write failed: {exc}")
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def get_measured_host(worker, file, ctx, ngl, ncm, kv):
|
|
457
|
+
try:
|
|
458
|
+
txt = _ie("SELECT json_build_object('anon', measured_host_anon, 'file', measured_host_file)::text FROM gguf_projection "
|
|
459
|
+
"WHERE worker=%s AND file=%s AND ctx=%d AND n_gpu_layers=%d AND n_cpu_moe=%d AND kv_type=%s"
|
|
460
|
+
% (_text(worker), _text(file), int(ctx), int(ngl), int(ncm), _text(kv)))
|
|
461
|
+
return json.loads(txt) if txt else None
|
|
462
|
+
except Exception: # noqa: BLE001
|
|
463
|
+
return None
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
def pair_facts(worker: str, key: str, file: str) -> dict:
|
|
467
|
+
"""Every FACT for (worker, model, quant) — hugpy.pair_facts(), the single
|
|
468
|
+
input of sizing.decide() (central uses the same function)."""
|
|
469
|
+
try:
|
|
470
|
+
txt = _psql_hugpy("SELECT pair_facts(%s, %s, %s)::text" % (_text(worker), _text(key), _text(file)))
|
|
471
|
+
return json.loads(txt) if txt else {}
|
|
472
|
+
except Exception as exc: # noqa: BLE001
|
|
473
|
+
print(f"[facts] read failed for {key}: {exc}")
|
|
474
|
+
return {}
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def put_tf_structure(worker, key, model_dir, tensor_bytes, tensor_bytes_bnb4, geometry):
|
|
478
|
+
"""T1 facts for a transformers model (safetensors headers + config geometry)."""
|
|
479
|
+
try:
|
|
480
|
+
_ie("INSERT INTO tf_structure (worker, key, model_dir, tensor_bytes, tensor_bytes_bnb4, geometry, at) "
|
|
481
|
+
"VALUES (%s, %s, %s, %s, %s, %s::jsonb, now()) ON CONFLICT (worker, key) DO UPDATE SET "
|
|
482
|
+
"model_dir=EXCLUDED.model_dir, tensor_bytes=EXCLUDED.tensor_bytes, tensor_bytes_bnb4=EXCLUDED.tensor_bytes_bnb4, "
|
|
483
|
+
"geometry=EXCLUDED.geometry, at=now()"
|
|
484
|
+
% (_text(worker), _text(key), _text(model_dir), _lit(tensor_bytes), _lit(tensor_bytes_bnb4),
|
|
485
|
+
_text(json.dumps(geometry or {}))))
|
|
486
|
+
except Exception as exc: # noqa: BLE001
|
|
487
|
+
print(f"[tf_structure] write failed: {exc}")
|