feat: add testing framework and initial test cases

- Updated package.json to include Vitest and testing dependencies.
- Created test cases for SuitePicker component to validate selection logic.
- Added tests for ReportsPage to ensure correct report grouping and ordering.
- Implemented read-only enforcement tests for SuitePage to prevent actions on built-in suites.
- Introduced a setup file for Vitest to include jest-dom matchers and cleanup after tests.
- Modified ReportsPage to group reports by model and display them accordingly.
- Enhanced API types to include suite_scope and question_count for better report handling.
This commit is contained in:
Netherwarlord
2026-07-28 20:06:26 -04:00
parent 8f246fabbc
commit 7a81468925
26 changed files with 2487 additions and 168 deletions
+169
View File
@@ -0,0 +1,169 @@
"""Minimal GGUF header reader.
Only enough of the format to answer "what kind of model is this file?" without
loading it. GGUF stores metadata as key/value pairs immediately after a 24-byte
header, and ``general.architecture`` is conventionally the first key, so the
answer usually arrives within the first few hundred bytes.
This exists to replace a filename heuristic. Filtering the model picker by name
would hide any legitimate model whose filename happened to contain "embed" —
worse than showing one that does not work, because the user cannot see why it
vanished. The architecture field states what the file actually is.
Spec: https://github.com/ggml-org/ggml/blob/master/docs/gguf.md
"""
from __future__ import annotations
import struct
from pathlib import Path
from typing import Any, Dict, Optional, Sequence
MAGIC = 0x46554747 # "GGUF" little-endian
# Value type tags, and the byte width of the fixed-size ones.
_UINT8, _INT8, _UINT16, _INT16, _UINT32, _INT32, _FLOAT32 = range(7)
_BOOL, _STRING, _ARRAY, _UINT64, _INT64, _FLOAT64 = range(7, 13)
_FIXED = {
_UINT8: ("<B", 1), _INT8: ("<b", 1), _BOOL: ("<?", 1),
_UINT16: ("<H", 2), _INT16: ("<h", 2),
_UINT32: ("<I", 4), _INT32: ("<i", 4), _FLOAT32: ("<f", 4),
_UINT64: ("<Q", 8), _INT64: ("<q", 8), _FLOAT64: ("<d", 8),
}
#: Give up rather than walk a pathological file. Architecture is key one.
_MAX_PAIRS = 512
class GGUFError(Exception):
"""The file is not readable as GGUF."""
class _Reader:
"""Buffered forward reader. Refills as needed so we never slurp the file."""
def __init__(self, handle, chunk: int = 1 << 16) -> None:
self._handle = handle
self._chunk = chunk
self._buf = b""
self._pos = 0
def _need(self, count: int) -> None:
while len(self._buf) - self._pos < count:
more = self._handle.read(max(self._chunk, count))
if not more:
raise GGUFError("unexpected end of file")
self._buf = self._buf[self._pos :] + more
self._pos = 0
def take(self, count: int) -> bytes:
self._need(count)
out = self._buf[self._pos : self._pos + count]
self._pos += count
return out
def scalar(self, fmt: str, size: int) -> Any:
return struct.unpack(fmt, self.take(size))[0]
def string(self) -> str:
length = self.scalar("<Q", 8)
if length > 1 << 20:
raise GGUFError("implausible string length")
return self.take(length).decode("utf-8", "replace")
def _read_value(reader: _Reader, value_type: int) -> Any:
if value_type in _FIXED:
fmt, size = _FIXED[value_type]
return reader.scalar(fmt, size)
if value_type == _STRING:
return reader.string()
if value_type == _ARRAY:
item_type = reader.scalar("<I", 4)
count = reader.scalar("<Q", 8)
if item_type in _FIXED:
# Skip wholesale; array contents are never needed here.
_, size = _FIXED[item_type]
reader.take(size * count)
return []
if item_type == _STRING:
for _ in range(count):
reader.string()
return []
raise GGUFError(f"unsupported array element type {item_type}")
raise GGUFError(f"unsupported value type {value_type}")
def read_metadata(path: Path, wanted: Sequence[str] = ("general.architecture",)) -> Dict[str, Any]:
"""Return the requested metadata keys, stopping as soon as all are found.
Never raises for an unreadable or non-GGUF file — returns ``{}`` instead, so
a caller can fall back rather than break model discovery over one odd file.
"""
remaining = set(wanted)
found: Dict[str, Any] = {}
try:
with open(path, "rb") as handle:
reader = _Reader(handle)
if reader.scalar("<I", 4) != MAGIC:
return {}
reader.scalar("<I", 4) # format version
reader.scalar("<Q", 8) # tensor count
pair_count = reader.scalar("<Q", 8)
for _ in range(min(pair_count, _MAX_PAIRS)):
key = reader.string()
value = _read_value(reader, reader.scalar("<I", 4))
if key in remaining:
found[key] = value
remaining.discard(key)
if not remaining:
break
except (OSError, struct.error, GGUFError, ValueError, MemoryError):
return found
return found
def architecture(path: Path) -> Optional[str]:
"""``general.architecture`` for a GGUF file, or None if unreadable."""
value = read_metadata(path).get("general.architecture")
return str(value).lower() if isinstance(value, str) else None
#: llama.cpp writes vision projectors with this architecture. They are a
#: companion to a multimodal model, never something to run on their own.
PROJECTOR_ARCHITECTURES = frozenset({"clip"})
GENERATIVE = "generative"
EMBEDDING = "embedding"
PROJECTOR = "projector"
UNKNOWN = "unknown"
def classify(path: Path) -> str:
"""What kind of model a GGUF file holds.
Decided from metadata rather than the filename. A name-based rule would
hide any legitimate model whose filename happened to contain "embed", and a
model silently missing from the picker is harder to diagnose than one that
fails when run.
``UNKNOWN`` is returned when the header cannot be read, and callers should
treat that as runnable — only positive evidence should exclude a model.
"""
arch = architecture(path)
if arch is None:
return UNKNOWN
if arch in PROJECTOR_ARCHITECTURES:
return PROJECTOR
# Embedding models declare a pooling strategy; generative ones do not.
# Checked in preference to an architecture blocklist, which would need
# updating for every new BERT variant.
if f"{arch}.pooling_type" in read_metadata(path, (f"{arch}.pooling_type",)):
return EMBEDDING
return GENERATIVE