mirror of
https://github.com/elder-plinius/OBLITERATUS.git
synced 2026-08-29 22:20:36 +02:00
feat: make UI model loading configurable
This commit is contained in:
@@ -540,6 +540,15 @@ This sets `CUDA_VISIBLE_DEVICES` before CUDA initializes. The model is then shar
|
||||
|
||||
The `--dtype` flag controls the precision of model weights, which directly determines how much VRAM you need. Lower precision means smaller memory footprint at the cost of some numerical fidelity:
|
||||
|
||||
The web UI exposes the same choice as **Quantization** and **Compute dtype** controls on Obliterate, Benchmark, Multi-Model, and Strength Sweep runs. `Auto (default)` preserves the historical behavior: FP16 compute plus automatic 4-bit loading when the memory estimate or known-model fallback says it is needed. Choose **None + BF16** (or FP16) when the native checkpoint fits in available VRAM and you are modifying weights; native floating-point weights generally provide better numerical fidelity for surgery. Quantized loading remains the right choice when fitting the model is the primary constraint. The resolved mode is printed before work starts and stored with results so it can be checked after the run.
|
||||
|
||||
CLI users can make the equivalent explicit selection with, for example:
|
||||
|
||||
```bash
|
||||
obliteratus obliterate Qwen/Qwen3.8-27B --dtype bfloat16
|
||||
obliteratus obliterate Qwen/Qwen3.8-27B --dtype float16 --quantization 4bit
|
||||
```
|
||||
|
||||
Install the optional backend before selecting a bitsandbytes mode:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -60,6 +60,12 @@ import torch
|
||||
from obliteratus import device as dev
|
||||
from obliteratus.credential_sources import resolve_first, resolve_secret, secret_available
|
||||
from obliteratus.ui_vram import render_vram_html
|
||||
from obliteratus.model_load_settings import (
|
||||
DTYPE_CHOICES,
|
||||
QUANTIZATION_CHOICES,
|
||||
ModelLoadSettings,
|
||||
resolve_model_load_settings,
|
||||
)
|
||||
from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer
|
||||
|
||||
# ── ZeroGPU support ─────────────────────────────────────────────────
|
||||
@@ -145,6 +151,41 @@ def _load_model_to_device(
|
||||
return model
|
||||
|
||||
|
||||
def _checkpoint_load_kwargs(load_settings: ModelLoadSettings) -> dict:
|
||||
"""Translate resolved settings for direct loading of a saved checkpoint."""
|
||||
torch_dtype = {
|
||||
"float16": torch.float16,
|
||||
"bfloat16": torch.bfloat16,
|
||||
"float32": torch.float32,
|
||||
}[load_settings.dtype]
|
||||
kwargs = {"torch_dtype": torch_dtype}
|
||||
if load_settings.quantization:
|
||||
from transformers import BitsAndBytesConfig
|
||||
if load_settings.quantization == "4bit":
|
||||
kwargs["quantization_config"] = BitsAndBytesConfig(
|
||||
load_in_4bit=True,
|
||||
bnb_4bit_compute_dtype=torch_dtype,
|
||||
bnb_4bit_quant_type="nf4",
|
||||
llm_int8_enable_fp32_cpu_offload=True,
|
||||
)
|
||||
else:
|
||||
kwargs["quantization_config"] = BitsAndBytesConfig(
|
||||
load_in_8bit=True,
|
||||
llm_int8_enable_fp32_cpu_offload=True,
|
||||
)
|
||||
return kwargs
|
||||
|
||||
|
||||
def _settings_from_metadata(metadata: dict | None) -> ModelLoadSettings:
|
||||
"""Restore concrete settings without re-running model auto-detection."""
|
||||
saved = metadata or {}
|
||||
return resolve_model_load_settings(
|
||||
saved.get("requested_quantization", "Auto (default)"),
|
||||
saved.get("requested_dtype", "Auto (default)"),
|
||||
auto_quantization=lambda _dtype: saved.get("quantization"),
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Global state
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -161,6 +202,7 @@ _state: dict = {
|
||||
# Checkpoint directory for ZeroGPU reload (model tensors may become stale
|
||||
# after GPU deallocation — this path lets chat_respond reload from disk)
|
||||
"output_dir": None,
|
||||
"load_settings": {},
|
||||
}
|
||||
_lock = threading.Lock()
|
||||
|
||||
@@ -233,6 +275,7 @@ def _recover_sessions_from_disk() -> None:
|
||||
"prompt_volume": data.get("prompt_volume", 0),
|
||||
"output_dir": str(p),
|
||||
"source": data.get("source", "recovered"),
|
||||
"load_settings": data.get("load_settings", {}),
|
||||
}
|
||||
found_any = True
|
||||
# Track the latest for auto-select
|
||||
@@ -254,6 +297,7 @@ def _recover_sessions_from_disk() -> None:
|
||||
_state["output_dir"] = _session_models[latest]["output_dir"]
|
||||
_state["model_name"] = _session_models[latest].get("model_choice")
|
||||
_state["method"] = _session_models[latest].get("method")
|
||||
_state["load_settings"] = _session_models[latest].get("load_settings", {})
|
||||
|
||||
|
||||
# Run recovery on import (app startup)
|
||||
@@ -716,7 +760,11 @@ _NEEDS_QUANTIZATION = {
|
||||
}
|
||||
|
||||
|
||||
def _should_quantize(model_id: str, is_preset: bool = False) -> str | None:
|
||||
def _should_quantize(
|
||||
model_id: str,
|
||||
is_preset: bool = False,
|
||||
dtype: str = "float16",
|
||||
) -> str | None:
|
||||
"""Return '4bit' if the model needs quantization for available GPU, else None."""
|
||||
try:
|
||||
from obliteratus.models.loader import _estimate_model_memory_gb, _available_gpu_memory_gb
|
||||
@@ -726,7 +774,12 @@ def _should_quantize(model_id: str, is_preset: bool = False) -> str | None:
|
||||
# Skip if model already ships with native quantization (e.g. Mxfp4Config)
|
||||
if getattr(config, "quantization_config", None) is not None:
|
||||
return None
|
||||
est_gb = _estimate_model_memory_gb(config, torch.float16)
|
||||
torch_dtype = {
|
||||
"float16": torch.float16,
|
||||
"bfloat16": torch.bfloat16,
|
||||
"float32": torch.float32,
|
||||
}[dtype]
|
||||
est_gb = _estimate_model_memory_gb(config, torch_dtype)
|
||||
gpu_gb = _available_gpu_memory_gb()
|
||||
if gpu_gb > 0 and est_gb > gpu_gb * 0.85:
|
||||
return "4bit"
|
||||
@@ -738,6 +791,36 @@ def _should_quantize(model_id: str, is_preset: bool = False) -> str | None:
|
||||
return None
|
||||
|
||||
|
||||
def _resolve_ui_load_settings(
|
||||
model_id: str,
|
||||
is_preset: bool,
|
||||
quantization_choice: str | None,
|
||||
dtype_choice: str | None,
|
||||
) -> ModelLoadSettings:
|
||||
"""Resolve UI controls while retaining the historical Auto behavior."""
|
||||
settings = resolve_model_load_settings(
|
||||
quantization_choice,
|
||||
dtype_choice,
|
||||
auto_quantization=lambda dtype: _should_quantize(
|
||||
model_id, is_preset=is_preset, dtype=dtype,
|
||||
),
|
||||
)
|
||||
target = dev.get_device("auto")
|
||||
if settings.requested_dtype == "BF16" and not dev.supports_bfloat16(target):
|
||||
raise ValueError(
|
||||
f"BF16 is not supported on {target!r}. Choose FP16 or FP32.",
|
||||
)
|
||||
if (
|
||||
settings.requested_quantization in {"4-bit", "8-bit"}
|
||||
and not dev.supports_bitsandbytes(target)
|
||||
):
|
||||
raise ValueError(
|
||||
f"{settings.requested_quantization} loading requires an NVIDIA CUDA GPU "
|
||||
"and the optional bitsandbytes dependency.",
|
||||
)
|
||||
return settings
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Obliteration
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -771,6 +854,7 @@ def _clear_stale_model_state() -> None:
|
||||
"status": "idle",
|
||||
"steering": None,
|
||||
"output_dir": None,
|
||||
"load_settings": {},
|
||||
})
|
||||
|
||||
|
||||
@@ -991,7 +1075,12 @@ _BENCH_TEST_PROMPTS = [
|
||||
]
|
||||
|
||||
|
||||
def _format_obliteration_metrics(pipeline, method: str, elapsed_str: str) -> str:
|
||||
def _format_obliteration_metrics(
|
||||
pipeline,
|
||||
method: str,
|
||||
elapsed_str: str,
|
||||
load_settings: ModelLoadSettings | None = None,
|
||||
) -> str:
|
||||
"""Format post-obliteration quality metrics as a concise Markdown card."""
|
||||
metrics = getattr(pipeline, "_quality_metrics", {}) or {}
|
||||
ppl = metrics.get("perplexity")
|
||||
@@ -1020,6 +1109,9 @@ def _format_obliteration_metrics(pipeline, method: str, elapsed_str: str) -> str
|
||||
parts.append(f"| KL Divergence | **{kl:.4f}** | {icon} |")
|
||||
if n_layers > 0:
|
||||
parts.append(f"| Layers Modified | **{n_layers}** | |")
|
||||
if load_settings is not None:
|
||||
parts.append(f"| Quantization | **{load_settings.quantization or 'none'}** | |")
|
||||
parts.append(f"| Compute dtype | **{load_settings.dtype}** | |")
|
||||
|
||||
if not metrics:
|
||||
return ""
|
||||
@@ -1132,6 +1224,8 @@ def benchmark(
|
||||
methods_to_test: list[str],
|
||||
prompt_volume_choice: str,
|
||||
dataset_source_choice: str = "",
|
||||
quantization_choice: str = "Auto (default)",
|
||||
dtype_choice: str = "Auto (default)",
|
||||
progress=gr.Progress(),
|
||||
):
|
||||
"""Run multiple abliteration methods on a single model and compare results.
|
||||
@@ -1148,6 +1242,13 @@ def benchmark(
|
||||
is_preset = model_choice in MODELS
|
||||
prompt_volume = PROMPT_VOLUMES.get(prompt_volume_choice, 33)
|
||||
dataset_key = get_source_key_from_label(dataset_source_choice) if dataset_source_choice else "builtin"
|
||||
try:
|
||||
load_settings = _resolve_ui_load_settings(
|
||||
model_id, is_preset, quantization_choice, dtype_choice,
|
||||
)
|
||||
except ValueError as exc:
|
||||
yield f"**Error:** {exc}", "", str(exc), None
|
||||
return
|
||||
|
||||
if not methods_to_test:
|
||||
methods_to_test = ["basic", "advanced", "surgical"]
|
||||
@@ -1184,6 +1285,7 @@ def benchmark(
|
||||
all_logs.append(f"Methods: {', '.join(methods_to_test)}")
|
||||
all_logs.append(f"Dataset: {source_label} ({len(harmful_all)} prompts available)")
|
||||
all_logs.append(f"Prompt volume: {vol_label} (using {actual_n} pairs)")
|
||||
all_logs.append(f"Resolved load mode: {load_settings.summary}")
|
||||
all_logs.append("=" * 60)
|
||||
|
||||
yield "**Starting benchmark...**", "", "\n".join(all_logs), None
|
||||
@@ -1220,7 +1322,7 @@ def benchmark(
|
||||
if result.status == "running":
|
||||
run_logs.append(f"{stage_key.upper()} — {result.message}")
|
||||
|
||||
quantization = _should_quantize(model_id, is_preset=is_preset)
|
||||
quantization = load_settings.quantization
|
||||
|
||||
def run_pipeline():
|
||||
try:
|
||||
@@ -1235,7 +1337,7 @@ def benchmark(
|
||||
model_name=model_id,
|
||||
output_dir=f"/tmp/bench_{method_key}",
|
||||
device="auto",
|
||||
dtype="float16",
|
||||
dtype=load_settings.dtype,
|
||||
quantization=quantization,
|
||||
trust_remote_code=is_preset,
|
||||
harmful_prompts=harmful_all[:n],
|
||||
@@ -1251,7 +1353,7 @@ def benchmark(
|
||||
model_name=model_id,
|
||||
output_dir=f"/tmp/bench_{method_key}",
|
||||
device="auto",
|
||||
dtype="float16",
|
||||
dtype=load_settings.dtype,
|
||||
method=method_key,
|
||||
quantization=quantization,
|
||||
trust_remote_code=is_preset,
|
||||
@@ -1291,6 +1393,8 @@ def benchmark(
|
||||
"model": model_id,
|
||||
"time_s": round(elapsed, 1),
|
||||
"error": None,
|
||||
"quantization": quantization,
|
||||
"dtype": load_settings.dtype,
|
||||
}
|
||||
|
||||
if run_error is not None:
|
||||
@@ -1343,6 +1447,7 @@ def benchmark(
|
||||
dataset=source_label,
|
||||
n_prompts=actual_n,
|
||||
quantization=quantization,
|
||||
compute_dtype=load_settings.dtype,
|
||||
)
|
||||
except Exception:
|
||||
pass # Telemetry is best-effort, never block benchmarks
|
||||
@@ -1358,6 +1463,7 @@ def benchmark(
|
||||
"method": method_key,
|
||||
"dataset_key": dataset_key,
|
||||
"prompt_volume": prompt_volume,
|
||||
"load_settings": load_settings.metadata(),
|
||||
"output_dir": bench_save_path,
|
||||
}
|
||||
_persist_session_meta(bench_save_path, label, {
|
||||
@@ -1366,6 +1472,7 @@ def benchmark(
|
||||
"method": method_key,
|
||||
"dataset_key": dataset_key,
|
||||
"prompt_volume": prompt_volume,
|
||||
"load_settings": load_settings.metadata(),
|
||||
"source": "benchmark",
|
||||
})
|
||||
|
||||
@@ -1492,6 +1599,8 @@ def benchmark_multi_model(
|
||||
method_choice: str,
|
||||
prompt_volume_choice: str,
|
||||
dataset_source_choice: str = "",
|
||||
quantization_choice: str = "Auto (default)",
|
||||
dtype_choice: str = "Auto (default)",
|
||||
progress=gr.Progress(),
|
||||
):
|
||||
"""Run one abliteration method across multiple models and compare.
|
||||
@@ -1578,7 +1687,16 @@ def benchmark_multi_model(
|
||||
def on_stage(result):
|
||||
pass
|
||||
|
||||
quantization = _should_quantize(model_id, is_preset=is_preset_model)
|
||||
try:
|
||||
load_settings = _resolve_ui_load_settings(
|
||||
model_id, is_preset_model, quantization_choice, dtype_choice,
|
||||
)
|
||||
except ValueError as exc:
|
||||
all_logs.append(f" ERROR: {exc}")
|
||||
yield f"**Error:** {exc}", _format_multi_model_results(results, bench_context), "\n".join(all_logs), None
|
||||
return
|
||||
quantization = load_settings.quantization
|
||||
all_logs.append(f"Resolved load mode: {load_settings.summary}")
|
||||
|
||||
def run_pipeline():
|
||||
try:
|
||||
@@ -1590,7 +1708,7 @@ def benchmark_multi_model(
|
||||
model_name=model_id,
|
||||
output_dir=f"/tmp/bench_mm_{mi}",
|
||||
device="auto",
|
||||
dtype="float16",
|
||||
dtype=load_settings.dtype,
|
||||
quantization=quantization,
|
||||
trust_remote_code=is_preset_model,
|
||||
harmful_prompts=harmful_all[:n],
|
||||
@@ -1606,7 +1724,7 @@ def benchmark_multi_model(
|
||||
model_name=model_id,
|
||||
output_dir=f"/tmp/bench_mm_{mi}",
|
||||
device="auto",
|
||||
dtype="float16",
|
||||
dtype=load_settings.dtype,
|
||||
method=method_key,
|
||||
quantization=quantization,
|
||||
trust_remote_code=is_preset_model,
|
||||
@@ -1645,6 +1763,8 @@ def benchmark_multi_model(
|
||||
"method": method_key,
|
||||
"time_s": round(elapsed, 1),
|
||||
"error": None,
|
||||
"quantization": quantization,
|
||||
"dtype": load_settings.dtype,
|
||||
}
|
||||
|
||||
if run_error is not None:
|
||||
@@ -1695,6 +1815,7 @@ def benchmark_multi_model(
|
||||
dataset=source_label,
|
||||
n_prompts=actual_n,
|
||||
quantization=quantization,
|
||||
compute_dtype=load_settings.dtype,
|
||||
)
|
||||
except Exception:
|
||||
pass # Telemetry is best-effort
|
||||
@@ -1710,6 +1831,7 @@ def benchmark_multi_model(
|
||||
"method": method_key,
|
||||
"dataset_key": dataset_key,
|
||||
"prompt_volume": prompt_volume,
|
||||
"load_settings": load_settings.metadata(),
|
||||
"output_dir": mm_save_path,
|
||||
}
|
||||
_persist_session_meta(mm_save_path, label, {
|
||||
@@ -1718,6 +1840,7 @@ def benchmark_multi_model(
|
||||
"method": method_key,
|
||||
"dataset_key": dataset_key,
|
||||
"prompt_volume": prompt_volume,
|
||||
"load_settings": load_settings.metadata(),
|
||||
"source": "benchmark_mm",
|
||||
})
|
||||
|
||||
@@ -1832,6 +1955,7 @@ def _format_multi_model_results(results: list[dict], context: dict | None = None
|
||||
def obliterate(model_choice: str, method_choice: str,
|
||||
prompt_volume_choice: str, dataset_source_choice: str,
|
||||
custom_harmful: str, custom_harmless: str,
|
||||
quantization_choice: str, dtype_choice: str,
|
||||
# Advanced params (sliders + radio)
|
||||
adv_n_directions: int, adv_direction_method: str,
|
||||
adv_regularization: float,
|
||||
@@ -1867,6 +1991,13 @@ def obliterate(model_choice: str, method_choice: str,
|
||||
is_preset = model_choice in MODELS
|
||||
method = METHODS.get(method_choice, "advanced")
|
||||
prompt_volume = PROMPT_VOLUMES.get(prompt_volume_choice, 33)
|
||||
try:
|
||||
load_settings = _resolve_ui_load_settings(
|
||||
model_id, is_preset, quantization_choice, dtype_choice,
|
||||
)
|
||||
except ValueError as exc:
|
||||
yield f"**Error:** {exc}", str(exc), get_chat_header(), gr.update(), gr.update(), gr.update()
|
||||
return
|
||||
|
||||
# Resolve "adaptive" → telemetry-recommended method for this model
|
||||
_adaptive_info = ""
|
||||
@@ -1956,7 +2087,7 @@ def obliterate(model_choice: str, method_choice: str,
|
||||
idx = stage_order.get(stage_key, 0)
|
||||
progress((idx + 1) / 6, desc=f"{stage_key.upper()}")
|
||||
|
||||
quantization = _should_quantize(model_id, is_preset=is_preset)
|
||||
quantization = load_settings.quantization
|
||||
|
||||
def run_pipeline():
|
||||
try:
|
||||
@@ -1985,7 +2116,7 @@ def obliterate(model_choice: str, method_choice: str,
|
||||
model_name=model_id,
|
||||
output_dir=save_dir,
|
||||
device="auto",
|
||||
dtype="float16",
|
||||
dtype=load_settings.dtype,
|
||||
quantization=quantization,
|
||||
trust_remote_code=is_preset,
|
||||
harmful_prompts=harmful_all[:n],
|
||||
@@ -2001,7 +2132,7 @@ def obliterate(model_choice: str, method_choice: str,
|
||||
model_name=model_id,
|
||||
output_dir=save_dir,
|
||||
device="auto",
|
||||
dtype="float16",
|
||||
dtype=load_settings.dtype,
|
||||
method=method,
|
||||
quantization=quantization,
|
||||
trust_remote_code=is_preset,
|
||||
@@ -2065,8 +2196,7 @@ def obliterate(model_choice: str, method_choice: str,
|
||||
log_lines.append(f"Dataset: {source_label}")
|
||||
vol_label = "all" if prompt_volume == -1 else str(prompt_volume)
|
||||
log_lines.append(f"Prompt volume: {vol_label} pairs")
|
||||
if quantization:
|
||||
log_lines.append(f"Quantization: {quantization} (auto-detected for GPU fit)")
|
||||
log_lines.append(f"Resolved load mode: {load_settings.summary}")
|
||||
log_lines.append("")
|
||||
|
||||
worker = threading.Thread(target=run_pipeline, daemon=True)
|
||||
@@ -2135,6 +2265,7 @@ def obliterate(model_choice: str, method_choice: str,
|
||||
dataset=ds_label,
|
||||
n_prompts=prompt_volume,
|
||||
quantization=quantization,
|
||||
compute_dtype=load_settings.dtype,
|
||||
)
|
||||
maybe_send_pipeline_report(pipeline)
|
||||
except Exception:
|
||||
@@ -2168,9 +2299,11 @@ def obliterate(model_choice: str, method_choice: str,
|
||||
"prompt_volume": prompt_volume,
|
||||
"output_dir": save_dir,
|
||||
"source": "obliterate",
|
||||
"load_settings": load_settings.metadata(),
|
||||
}
|
||||
_state["steering"] = steering_meta
|
||||
_state["output_dir"] = save_dir # for ZeroGPU checkpoint reload
|
||||
_state["load_settings"] = load_settings.metadata()
|
||||
|
||||
# Persist session metadata to disk so we survive ZeroGPU process restarts
|
||||
_persist_session_meta(save_dir, _cache_label, {
|
||||
@@ -2180,6 +2313,7 @@ def obliterate(model_choice: str, method_choice: str,
|
||||
"dataset_key": dataset_key if not use_custom else "custom",
|
||||
"prompt_volume": prompt_volume,
|
||||
"source": "obliterate",
|
||||
"load_settings": load_settings.metadata(),
|
||||
})
|
||||
|
||||
if can_generate:
|
||||
@@ -2261,7 +2395,11 @@ def obliterate(model_choice: str, method_choice: str,
|
||||
model_reloaded = _load_model_to_device(
|
||||
save_dir,
|
||||
offload_folder=offload_dir,
|
||||
torch_dtype=torch.float16,
|
||||
torch_dtype={
|
||||
"float16": torch.float16,
|
||||
"bfloat16": torch.bfloat16,
|
||||
"float32": torch.float32,
|
||||
}[load_settings.dtype],
|
||||
trust_remote_code=True,
|
||||
)
|
||||
tokenizer_reloaded = AutoTokenizer.from_pretrained(
|
||||
@@ -2290,7 +2428,9 @@ def obliterate(model_choice: str, method_choice: str,
|
||||
_state["status"] = "idle"
|
||||
|
||||
# Build metrics summary card while pipeline is still alive
|
||||
metrics_card = _format_obliteration_metrics(pipeline, method, _elapsed())
|
||||
metrics_card = _format_obliteration_metrics(
|
||||
pipeline, method, _elapsed(), load_settings,
|
||||
)
|
||||
|
||||
# Free pipeline internals we no longer need (activations, directions cache)
|
||||
# to reclaim memory — we've already extracted the model and steering metadata.
|
||||
@@ -2420,9 +2560,11 @@ def chat_respond(message: str, history: list[dict], system_prompt: str,
|
||||
if _checkpoint_is_available(checkpoint):
|
||||
try:
|
||||
is_preset = (_state.get("model_name") or "") in MODELS
|
||||
load_settings = _settings_from_metadata(_state.get("load_settings"))
|
||||
model = _load_model_to_device(
|
||||
checkpoint, torch_dtype=torch.float16,
|
||||
checkpoint,
|
||||
trust_remote_code=is_preset,
|
||||
**_checkpoint_load_kwargs(load_settings),
|
||||
)
|
||||
tokenizer = AutoTokenizer.from_pretrained(
|
||||
checkpoint, trust_remote_code=is_preset,
|
||||
@@ -2631,9 +2773,11 @@ def load_bench_into_chat(choice: str, progress=gr.Progress()):
|
||||
if checkpoint and Path(checkpoint).exists():
|
||||
is_preset = (_state.get("model_name") or "") in MODELS
|
||||
try:
|
||||
load_settings = _settings_from_metadata(_state.get("load_settings"))
|
||||
model_loaded = _load_model_to_device(
|
||||
checkpoint, torch_dtype=torch.float16,
|
||||
checkpoint,
|
||||
trust_remote_code=is_preset,
|
||||
**_checkpoint_load_kwargs(load_settings),
|
||||
)
|
||||
tokenizer_loaded = AutoTokenizer.from_pretrained(
|
||||
checkpoint, trust_remote_code=is_preset,
|
||||
@@ -2663,6 +2807,11 @@ def load_bench_into_chat(choice: str, progress=gr.Progress()):
|
||||
model_id = cfg["model_id"]
|
||||
method_key = cfg["method"]
|
||||
checkpoint_dir = cfg.get("output_dir")
|
||||
try:
|
||||
load_settings = _settings_from_metadata(cfg.get("load_settings"))
|
||||
except ValueError as exc:
|
||||
yield f"**Error:** Saved model load settings are invalid: {exc}", get_chat_header()
|
||||
return
|
||||
|
||||
# If this model is already the active one, skip the destructive reload
|
||||
with _lock:
|
||||
@@ -2683,6 +2832,7 @@ def load_bench_into_chat(choice: str, progress=gr.Progress()):
|
||||
_state["status"] = "obliterating"
|
||||
_state["model_name"] = cfg["model_choice"]
|
||||
_state["method"] = method_key
|
||||
_state["load_settings"] = load_settings.metadata()
|
||||
_clear_gpu()
|
||||
|
||||
# If we have a saved checkpoint on disk, load directly — no re-training!
|
||||
@@ -2694,8 +2844,8 @@ def load_bench_into_chat(choice: str, progress=gr.Progress()):
|
||||
try:
|
||||
model_loaded = _load_model_to_device(
|
||||
checkpoint_dir,
|
||||
torch_dtype=torch.float16,
|
||||
trust_remote_code=is_preset,
|
||||
**_checkpoint_load_kwargs(load_settings),
|
||||
)
|
||||
tokenizer_loaded = AutoTokenizer.from_pretrained(
|
||||
checkpoint_dir, trust_remote_code=is_preset,
|
||||
@@ -2715,6 +2865,16 @@ def load_bench_into_chat(choice: str, progress=gr.Progress()):
|
||||
)
|
||||
return
|
||||
except Exception:
|
||||
if load_settings.requested_quantization != "Auto (default)":
|
||||
_clear_gpu()
|
||||
with _lock:
|
||||
_state["status"] = "idle"
|
||||
yield (
|
||||
f"**Error:** Could not load `{choice}` with the requested "
|
||||
f"settings ({load_settings.summary}).",
|
||||
get_chat_header(),
|
||||
)
|
||||
return
|
||||
# Checkpoint load failed (e.g. GPU too small at fp16) — try 4-bit
|
||||
_clear_gpu()
|
||||
try:
|
||||
@@ -2771,7 +2931,7 @@ def load_bench_into_chat(choice: str, progress=gr.Progress()):
|
||||
n = min(len(harmful_all), len(harmless_all))
|
||||
|
||||
is_preset = cfg["model_choice"] in MODELS
|
||||
quantization = _should_quantize(model_id, is_preset=is_preset)
|
||||
quantization = load_settings.quantization
|
||||
|
||||
pipeline_ref = [None]
|
||||
error_ref = [None]
|
||||
@@ -2783,7 +2943,7 @@ def load_bench_into_chat(choice: str, progress=gr.Progress()):
|
||||
model_name=model_id,
|
||||
output_dir="/tmp/obliterated",
|
||||
device="auto",
|
||||
dtype="float16",
|
||||
dtype=load_settings.dtype,
|
||||
method=method_key,
|
||||
quantization=quantization,
|
||||
trust_remote_code=is_preset,
|
||||
@@ -2871,9 +3031,11 @@ def ab_chat_respond(message: str, history_left: list[dict], history_right: list[
|
||||
if checkpoint and Path(checkpoint).exists():
|
||||
try:
|
||||
is_preset = (model_name or "") in MODELS
|
||||
load_settings = _settings_from_metadata(_state.get("load_settings"))
|
||||
abliterated_model = _load_model_to_device(
|
||||
checkpoint, torch_dtype=torch.float16,
|
||||
checkpoint,
|
||||
trust_remote_code=is_preset,
|
||||
**_checkpoint_load_kwargs(load_settings),
|
||||
)
|
||||
tokenizer = AutoTokenizer.from_pretrained(
|
||||
checkpoint, trust_remote_code=is_preset,
|
||||
@@ -3002,10 +3164,12 @@ def ab_chat_respond(message: str, history_left: list[dict], history_right: list[
|
||||
is_preset = model_name in MODELS
|
||||
original_response = ""
|
||||
try:
|
||||
load_settings = _settings_from_metadata(_state.get("load_settings"))
|
||||
original_model = _load_model_to_device(
|
||||
model_id, torch_dtype=torch.float16,
|
||||
model_id,
|
||||
trust_remote_code=is_preset,
|
||||
low_cpu_mem_usage=True,
|
||||
**_checkpoint_load_kwargs(load_settings),
|
||||
token=resolve_secret("HF_TOKEN"),
|
||||
)
|
||||
|
||||
@@ -3074,7 +3238,10 @@ def ab_chat_respond(message: str, history_left: list[dict], history_right: list[
|
||||
@spaces.GPU(duration=300)
|
||||
def strength_sweep(model_choice: str, method_choice: str,
|
||||
prompt_vol_choice: str, dataset_source_choice: str,
|
||||
sweep_steps: int, progress=gr.Progress()):
|
||||
sweep_steps: int,
|
||||
quantization_choice: str = "Auto (default)",
|
||||
dtype_choice: str = "Auto (default)",
|
||||
progress=gr.Progress()):
|
||||
"""Sweep regularization from 0.0→1.0 and measure refusal rate + perplexity.
|
||||
|
||||
Produces a dose-response curve: the fundamental plot for abliteration research.
|
||||
@@ -3086,13 +3253,21 @@ def strength_sweep(model_choice: str, method_choice: str,
|
||||
is_preset = model_choice in MODELS
|
||||
method_key = METHODS.get(method_choice, "advanced")
|
||||
dataset_key = get_source_key_from_label(dataset_source_choice) if dataset_source_choice else "builtin"
|
||||
try:
|
||||
load_settings = _resolve_ui_load_settings(
|
||||
model_id, is_preset, quantization_choice, dtype_choice,
|
||||
)
|
||||
except ValueError as exc:
|
||||
yield f"Error: {exc}", "", str(exc), None, None
|
||||
return
|
||||
|
||||
sweep_steps = max(3, min(int(sweep_steps), 20))
|
||||
regs = [round(i / (sweep_steps - 1), 3) for i in range(sweep_steps)]
|
||||
|
||||
results = []
|
||||
all_logs = [f"Ablation Strength Sweep: {model_choice} x {method_key}",
|
||||
f"Sweep points: {regs}", ""]
|
||||
f"Sweep points: {regs}",
|
||||
f"Resolved load mode: {load_settings.summary}", ""]
|
||||
|
||||
yield "Starting sweep...", "", "\n".join(all_logs), None, None
|
||||
|
||||
@@ -3121,12 +3296,12 @@ def strength_sweep(model_choice: str, method_choice: str,
|
||||
|
||||
def _run_sweep_point():
|
||||
try:
|
||||
quantization = _should_quantize(model_id, is_preset=is_preset)
|
||||
quantization = load_settings.quantization
|
||||
pipe = AbliterationPipeline(
|
||||
model_id, method=method_key,
|
||||
output_dir=f"/tmp/sweep_{step_i}",
|
||||
device="auto",
|
||||
dtype="float16",
|
||||
dtype=load_settings.dtype,
|
||||
quantization=quantization,
|
||||
trust_remote_code=is_preset,
|
||||
harmful_prompts=harmful, harmless_prompts=harmless,
|
||||
@@ -4076,6 +4251,18 @@ with gr.Blocks(title="OBLITERATUS", fill_height=True) as demo:
|
||||
label="Dataset Source",
|
||||
info="Built-in (512 pairs) or download larger research datasets from HuggingFace",
|
||||
)
|
||||
quantization_dd = gr.Dropdown(
|
||||
choices=list(QUANTIZATION_CHOICES),
|
||||
value="Auto (default)",
|
||||
label="Quantization",
|
||||
info="Auto preserves memory-based 4-bit selection. Choose None for native weights.",
|
||||
)
|
||||
dtype_dd = gr.Dropdown(
|
||||
choices=list(DTYPE_CHOICES),
|
||||
value="Auto (default)",
|
||||
label="Compute dtype",
|
||||
info="Auto preserves the existing FP16 behavior. BF16 is recommended for weight surgery when supported.",
|
||||
)
|
||||
dataset_info_md = gr.Markdown(
|
||||
f"*{DATASET_SOURCES['builtin'].description}*",
|
||||
elem_classes=["dataset-info"],
|
||||
@@ -4330,6 +4517,15 @@ result = client.predict(
|
||||
label="Dataset Source",
|
||||
info="Select prompt dataset for benchmarking",
|
||||
)
|
||||
with gr.Row():
|
||||
bench_quantization = gr.Dropdown(
|
||||
choices=list(QUANTIZATION_CHOICES), value="Auto (default)",
|
||||
label="Quantization",
|
||||
)
|
||||
bench_dtype = gr.Dropdown(
|
||||
choices=list(DTYPE_CHOICES), value="Auto (default)",
|
||||
label="Compute dtype",
|
||||
)
|
||||
bench_btn = gr.Button(
|
||||
"Run Multi-Method Benchmark",
|
||||
variant="primary", size="lg",
|
||||
@@ -4434,6 +4630,15 @@ result = client.predict(
|
||||
value=get_source_choices()[0],
|
||||
label="Dataset Source",
|
||||
)
|
||||
with gr.Row():
|
||||
mm_quantization = gr.Dropdown(
|
||||
choices=list(QUANTIZATION_CHOICES), value="Auto (default)",
|
||||
label="Quantization",
|
||||
)
|
||||
mm_dtype = gr.Dropdown(
|
||||
choices=list(DTYPE_CHOICES), value="Auto (default)",
|
||||
label="Compute dtype",
|
||||
)
|
||||
mm_btn = gr.Button(
|
||||
"Run Multi-Model Benchmark",
|
||||
variant="primary", size="lg",
|
||||
@@ -4764,6 +4969,15 @@ tradeoff point where refusal is minimized with minimal capability damage.
|
||||
label="Sweep Points",
|
||||
info="Number of regularization values to test (more = finer curve, slower)",
|
||||
)
|
||||
with gr.Row():
|
||||
sweep_quantization = gr.Dropdown(
|
||||
choices=list(QUANTIZATION_CHOICES), value="Auto (default)",
|
||||
label="Quantization",
|
||||
)
|
||||
sweep_dtype = gr.Dropdown(
|
||||
choices=list(DTYPE_CHOICES), value="Auto (default)",
|
||||
label="Compute dtype",
|
||||
)
|
||||
|
||||
sweep_btn = gr.Button("Run Sweep", variant="primary")
|
||||
sweep_status = gr.Markdown("")
|
||||
@@ -4781,7 +4995,8 @@ tradeoff point where refusal is minimized with minimal capability damage.
|
||||
sweep_btn.click(
|
||||
fn=strength_sweep,
|
||||
inputs=[sweep_model_dd, sweep_method_dd, sweep_vol_dd,
|
||||
sweep_dataset_dd, sweep_steps_slider],
|
||||
sweep_dataset_dd, sweep_steps_slider,
|
||||
sweep_quantization, sweep_dtype],
|
||||
outputs=[sweep_status, sweep_results, sweep_log, sweep_gallery,
|
||||
gr.State()], # 5th output is unused File placeholder
|
||||
)
|
||||
@@ -5194,7 +5409,8 @@ Built on the shoulders of:
|
||||
# Wire benchmark → Chat/A/B cross-tab dropdown updates
|
||||
bench_btn.click(
|
||||
fn=benchmark,
|
||||
inputs=[bench_model, bench_methods, bench_prompt_vol, bench_dataset],
|
||||
inputs=[bench_model, bench_methods, bench_prompt_vol, bench_dataset,
|
||||
bench_quantization, bench_dtype],
|
||||
outputs=[bench_status, bench_results, bench_log, bench_gallery],
|
||||
api_name="/benchmark",
|
||||
).then(
|
||||
@@ -5214,7 +5430,8 @@ Built on the shoulders of:
|
||||
|
||||
mm_btn.click(
|
||||
fn=benchmark_multi_model,
|
||||
inputs=[mm_models, mm_method, mm_prompt_vol, mm_dataset],
|
||||
inputs=[mm_models, mm_method, mm_prompt_vol, mm_dataset,
|
||||
mm_quantization, mm_dtype],
|
||||
outputs=[mm_status, mm_results, mm_log, mm_gallery],
|
||||
api_name="/benchmark_multi_model",
|
||||
).then(
|
||||
@@ -5239,7 +5456,8 @@ Built on the shoulders of:
|
||||
obliterate_btn.click(
|
||||
fn=obliterate,
|
||||
inputs=[model_dd, method_dd, prompt_vol_dd, dataset_dd,
|
||||
custom_harmful_tb, custom_harmless_tb] + _adv_controls,
|
||||
custom_harmful_tb, custom_harmless_tb,
|
||||
quantization_dd, dtype_dd] + _adv_controls,
|
||||
outputs=[status_md, log_box, chat_status, session_model_dd, metrics_md, ab_session_model_dd],
|
||||
).then(
|
||||
fn=lambda: _get_vram_html(),
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
],
|
||||
"paths": [
|
||||
"app.py",
|
||||
"obliteratus/model_load_settings.py",
|
||||
"obliteratus/__init__.py",
|
||||
"obliteratus/__main__.py",
|
||||
"obliteratus/analysis/__init__.py",
|
||||
@@ -33,6 +34,8 @@
|
||||
],
|
||||
"required_tests": [
|
||||
"tests/test_app_vram.py",
|
||||
"tests/test_app_model_load_settings.py",
|
||||
"tests/test_model_load_settings.py",
|
||||
"tests/test_cli.py",
|
||||
"tests/test_cli_boundaries.py",
|
||||
"tests/test_module_imports.py",
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
"""Resolve operator-facing model load choices into loader arguments."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import asdict, dataclass
|
||||
from typing import Callable
|
||||
|
||||
|
||||
QUANTIZATION_CHOICES = ("Auto (default)", "None", "4-bit", "8-bit")
|
||||
DTYPE_CHOICES = ("Auto (default)", "BF16", "FP16", "FP32")
|
||||
|
||||
_QUANTIZATION_VALUES = {
|
||||
"Auto (default)": "auto",
|
||||
"auto": "auto",
|
||||
"None": None,
|
||||
"none": None,
|
||||
"4-bit": "4bit",
|
||||
"4bit": "4bit",
|
||||
"8-bit": "8bit",
|
||||
"8bit": "8bit",
|
||||
}
|
||||
_DTYPE_VALUES = {
|
||||
"Auto (default)": "float16",
|
||||
"auto": "float16",
|
||||
"BF16": "bfloat16",
|
||||
"bfloat16": "bfloat16",
|
||||
"FP16": "float16",
|
||||
"float16": "float16",
|
||||
"FP32": "float32",
|
||||
"float32": "float32",
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ModelLoadSettings:
|
||||
"""Requested UI values and the concrete values sent to the loader."""
|
||||
|
||||
requested_quantization: str
|
||||
requested_dtype: str
|
||||
quantization: str | None
|
||||
dtype: str
|
||||
|
||||
@property
|
||||
def summary(self) -> str:
|
||||
quantization = self.quantization or "none"
|
||||
return f"quantization={quantization}, compute dtype={self.dtype}"
|
||||
|
||||
def metadata(self) -> dict[str, str | None]:
|
||||
return asdict(self)
|
||||
|
||||
|
||||
def resolve_model_load_settings(
|
||||
quantization_choice: str | None,
|
||||
dtype_choice: str | None,
|
||||
*,
|
||||
auto_quantization: Callable[[str], str | None],
|
||||
) -> ModelLoadSettings:
|
||||
"""Validate UI/API choices and return concrete pipeline load settings.
|
||||
|
||||
``Auto`` intentionally resolves dtype to ``float16`` because that was the
|
||||
web application's historical hard-coded value. Quantization Auto delegates
|
||||
to the existing memory-estimation and fallback policy supplied by the app.
|
||||
"""
|
||||
requested_quantization = quantization_choice or "Auto (default)"
|
||||
requested_dtype = dtype_choice or "Auto (default)"
|
||||
if requested_quantization not in _QUANTIZATION_VALUES:
|
||||
choices = ", ".join(QUANTIZATION_CHOICES)
|
||||
raise ValueError(f"Unsupported quantization choice {requested_quantization!r}. Choose: {choices}.")
|
||||
if requested_dtype not in _DTYPE_VALUES:
|
||||
choices = ", ".join(DTYPE_CHOICES)
|
||||
raise ValueError(f"Unsupported compute dtype {requested_dtype!r}. Choose: {choices}.")
|
||||
|
||||
dtype = _DTYPE_VALUES[requested_dtype]
|
||||
quantization = _QUANTIZATION_VALUES[requested_quantization]
|
||||
if quantization == "auto":
|
||||
quantization = auto_quantization(dtype)
|
||||
|
||||
return ModelLoadSettings(
|
||||
requested_quantization=requested_quantization,
|
||||
requested_dtype=requested_dtype,
|
||||
quantization=quantization,
|
||||
dtype=dtype,
|
||||
)
|
||||
@@ -361,6 +361,7 @@ class BenchmarkRecord:
|
||||
gpu_name: str = ""
|
||||
gpu_vram_gb: float = 0.0
|
||||
quantization: str | None = None
|
||||
compute_dtype: str = ""
|
||||
|
||||
# Extra metadata
|
||||
extra: dict[str, Any] = field(default_factory=dict)
|
||||
@@ -803,6 +804,7 @@ def log_benchmark_from_dict(
|
||||
dataset: str = "",
|
||||
n_prompts: int = 0,
|
||||
quantization: str | None = None,
|
||||
compute_dtype: str = "",
|
||||
pipeline_config: dict[str, Any] | None = None,
|
||||
) -> bool:
|
||||
"""Convenience wrapper: create a BenchmarkRecord from benchmark result dict.
|
||||
@@ -817,6 +819,7 @@ def log_benchmark_from_dict(
|
||||
dataset=dataset,
|
||||
n_prompts=n_prompts,
|
||||
quantization=quantization,
|
||||
compute_dtype=compute_dtype,
|
||||
refusal_rate=entry.get("refusal_rate"),
|
||||
perplexity=entry.get("perplexity"),
|
||||
coherence=entry.get("coherence"),
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
"""Web-application contracts for configurable model loading."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.operator_ui
|
||||
def test_ui_resolves_auto_explicit_bfloat16_and_invalid_hardware():
|
||||
"""Exercise app helpers in isolation from Gradio's import-time sockets."""
|
||||
script = r'''
|
||||
from unittest.mock import Mock
|
||||
import torch
|
||||
import app
|
||||
|
||||
app.dev.get_device = lambda _requested="auto": "cuda:0"
|
||||
app.dev.supports_bfloat16 = lambda _device=None: True
|
||||
app.dev.supports_bitsandbytes = lambda _device=None: True
|
||||
|
||||
automatic = Mock(return_value="4bit")
|
||||
app._should_quantize = automatic
|
||||
settings = app._resolve_ui_load_settings(
|
||||
"Qwen/Qwen3.8-27B", True, "Auto (default)", "Auto (default)",
|
||||
)
|
||||
assert settings.quantization == "4bit"
|
||||
assert settings.dtype == "float16"
|
||||
automatic.assert_called_once_with(
|
||||
"Qwen/Qwen3.8-27B", is_preset=True, dtype="float16",
|
||||
)
|
||||
|
||||
automatic = Mock(side_effect=AssertionError("automatic policy must not run"))
|
||||
app._should_quantize = automatic
|
||||
settings = app._resolve_ui_load_settings(
|
||||
"Qwen/Qwen3.8-27B", True, "None", "BF16",
|
||||
)
|
||||
assert settings.quantization is None
|
||||
assert settings.dtype == "bfloat16"
|
||||
assert app._checkpoint_load_kwargs(settings) == {"torch_dtype": torch.bfloat16}
|
||||
automatic.assert_not_called()
|
||||
|
||||
app.dev.supports_bfloat16 = lambda _device=None: False
|
||||
try:
|
||||
app._resolve_ui_load_settings("org/model", False, "None", "BF16")
|
||||
except ValueError as exc:
|
||||
assert "BF16 is not supported" in str(exc)
|
||||
assert "FP16 or FP32" in str(exc)
|
||||
else:
|
||||
raise AssertionError("unsupported BF16 must fail before pipeline construction")
|
||||
'''
|
||||
result = subprocess.run(
|
||||
[sys.executable, "-c", script],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=60,
|
||||
check=False,
|
||||
)
|
||||
assert result.returncode == 0, result.stdout + result.stderr
|
||||
@@ -0,0 +1,92 @@
|
||||
"""Contracts for explicit and automatic web-UI model loading settings."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import ast
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from obliteratus.model_load_settings import resolve_model_load_settings
|
||||
|
||||
|
||||
def _resolve(quantization="Auto (default)", dtype="Auto (default)", auto="4bit"):
|
||||
calls = []
|
||||
|
||||
def automatic(resolved_dtype):
|
||||
calls.append(resolved_dtype)
|
||||
return auto
|
||||
|
||||
return resolve_model_load_settings(
|
||||
quantization, dtype, auto_quantization=automatic,
|
||||
), calls
|
||||
|
||||
|
||||
def test_defaults_preserve_float16_and_automatic_quantization():
|
||||
settings, calls = _resolve()
|
||||
assert settings.dtype == "float16"
|
||||
assert settings.quantization == "4bit"
|
||||
assert calls == ["float16"]
|
||||
|
||||
|
||||
def test_explicit_bfloat16_without_quantization_bypasses_auto_policy():
|
||||
settings, calls = _resolve("None", "BF16")
|
||||
assert settings.dtype == "bfloat16"
|
||||
assert settings.quantization is None
|
||||
assert calls == []
|
||||
assert settings.summary == "quantization=none, compute dtype=bfloat16"
|
||||
|
||||
|
||||
def test_explicit_four_bit_and_dtype_are_preserved():
|
||||
settings, calls = _resolve("4-bit", "FP16", auto=None)
|
||||
assert settings.quantization == "4bit"
|
||||
assert settings.dtype == "float16"
|
||||
assert calls == []
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("quantization", "dtype", "match"),
|
||||
[
|
||||
("3-bit", "FP16", "Unsupported quantization choice"),
|
||||
("None", "TF32", "Unsupported compute dtype"),
|
||||
],
|
||||
)
|
||||
def test_invalid_choices_fail_early_with_actionable_errors(quantization, dtype, match):
|
||||
with pytest.raises(ValueError, match=match):
|
||||
_resolve(quantization, dtype)
|
||||
|
||||
|
||||
def test_every_auto_quantization_ui_entry_point_uses_resolved_settings():
|
||||
"""Guard every path that historically called app._should_quantize()."""
|
||||
tree = ast.parse(Path("app.py").read_text())
|
||||
functions = {
|
||||
node.name: node
|
||||
for node in ast.walk(tree)
|
||||
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))
|
||||
}
|
||||
|
||||
for name in ("benchmark", "benchmark_multi_model", "obliterate", "strength_sweep"):
|
||||
calls = {
|
||||
node.func.id
|
||||
for node in ast.walk(functions[name])
|
||||
if isinstance(node, ast.Call) and isinstance(node.func, ast.Name)
|
||||
}
|
||||
assert "_resolve_ui_load_settings" in calls, name
|
||||
|
||||
reload_calls = {
|
||||
node.func.id
|
||||
for node in ast.walk(functions["load_bench_into_chat"])
|
||||
if isinstance(node, ast.Call) and isinstance(node.func, ast.Name)
|
||||
}
|
||||
assert "_settings_from_metadata" in reload_calls
|
||||
|
||||
direct_auto_callers = []
|
||||
for name, function in functions.items():
|
||||
if any(
|
||||
isinstance(node, ast.Call)
|
||||
and isinstance(node.func, ast.Name)
|
||||
and node.func.id == "_should_quantize"
|
||||
for node in ast.walk(function)
|
||||
):
|
||||
direct_auto_callers.append(name)
|
||||
assert direct_auto_callers == ["_resolve_ui_load_settings"]
|
||||
@@ -908,6 +908,8 @@ class TestTelemetryHubBoundaries:
|
||||
{"refusal_rate": 0.0, "perplexity": None, "error": "partial"},
|
||||
dataset="fixture",
|
||||
n_prompts=4,
|
||||
quantization="4bit",
|
||||
compute_dtype="bfloat16",
|
||||
pipeline_config={"n_directions": 3, "bayesian_trials": 2},
|
||||
)
|
||||
record = write.call_args.args[0]
|
||||
@@ -916,6 +918,8 @@ class TestTelemetryHubBoundaries:
|
||||
assert record.error == "partial"
|
||||
assert record.n_directions == 3
|
||||
assert record.use_bayesian is True
|
||||
assert record.quantization == "4bit"
|
||||
assert record.compute_dtype == "bfloat16"
|
||||
|
||||
def test_push_to_hub_success_and_empty_short_circuits(self, tmp_path):
|
||||
import obliteratus.telemetry as telemetry
|
||||
|
||||
Reference in New Issue
Block a user