Prune hosted run controls
This commit is contained in:
parent
75dd1e7e6f
commit
f3dc8e0d52
5 changed files with 58 additions and 78 deletions
|
|
@ -210,7 +210,6 @@ const deviceField = byId<HTMLElement>("deviceField");
|
|||
const deviceInput = byId<HTMLSelectElement>("deviceInput");
|
||||
const stripBackgroundField = byId<HTMLElement>("stripBackgroundField");
|
||||
const stripBackgroundInput = byId<HTMLInputElement>("stripBackgroundInput");
|
||||
const skipRerankInput = byId<HTMLInputElement>("skipRerankInput");
|
||||
|
||||
const paintInputs = {
|
||||
r: byId<HTMLInputElement>("paintR"),
|
||||
|
|
@ -1493,7 +1492,7 @@ function buildFormData(): FormData {
|
|||
["seed", String(parseOptionalInteger(seedInput) ?? 7)],
|
||||
["device", runtimeConfig?.hostedDemo ? "cpu" : deviceInput.value],
|
||||
["strip_background", runtimeConfig?.hostedDemo ? "false" : stripBackgroundInput.checked ? "true" : "false"],
|
||||
["skip_candidate_rerank", skipRerankInput.checked ? "true" : "false"],
|
||||
["skip_candidate_rerank", "false"],
|
||||
];
|
||||
for (const [key, value] of values) {
|
||||
if (value !== null) {
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ class HostedDemoConfig:
|
|||
defaults = {
|
||||
"max_upload_bytes": 2 * 1_048_576 if hosted_demo else 16 * 1_048_576,
|
||||
"max_input_dimension": 2048 if hosted_demo else 4096,
|
||||
"max_output_dimension": 256 if hosted_demo else 1024,
|
||||
"max_output_dimension": 512 if hosted_demo else 1024,
|
||||
"default_steps": 32 if hosted_demo else 48,
|
||||
"max_steps": 48 if hosted_demo else 256,
|
||||
"queue_capacity": 10 if hosted_demo else 32,
|
||||
|
|
@ -515,7 +515,7 @@ def _normalize_job_options(
|
|||
normalized_strip_background = False if config.hosted_demo else bool(strip_background)
|
||||
inference_mode = "autocorr" if fixed_dims is None else "fixed"
|
||||
max_inferred_target_size = config.max_output_dimension if fixed_dims is None else None
|
||||
normalized_skip_candidate_rerank = bool(skip_candidate_rerank)
|
||||
normalized_skip_candidate_rerank = False
|
||||
return {
|
||||
"target_size": normalized_target_size,
|
||||
"target_width": normalized_target_width,
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@ const deviceField = byId("deviceField");
|
|||
const deviceInput = byId("deviceInput");
|
||||
const stripBackgroundField = byId("stripBackgroundField");
|
||||
const stripBackgroundInput = byId("stripBackgroundInput");
|
||||
const skipRerankInput = byId("skipRerankInput");
|
||||
const paintInputs = {
|
||||
r: byId("paintR"),
|
||||
g: byId("paintG"),
|
||||
|
|
@ -65,8 +64,8 @@ const eventTypes = [
|
|||
"stage_started",
|
||||
"source_loaded",
|
||||
"preprocess_completed",
|
||||
"lattice_inference_started",
|
||||
"lattice_inference_progress",
|
||||
"lattice_search_started",
|
||||
"lattice_search_progress",
|
||||
"inference_candidates_ready",
|
||||
"candidate_rerank_started",
|
||||
"candidate_rerank_candidate_started",
|
||||
|
|
@ -92,7 +91,7 @@ const state = {
|
|||
sourceImage: null,
|
||||
preprocessedImage: null,
|
||||
latticeImage: null,
|
||||
signalImage: null,
|
||||
guidanceImage: null,
|
||||
inference: null,
|
||||
inferenceMode: null,
|
||||
latticeSearch: null,
|
||||
|
|
@ -311,7 +310,7 @@ function applyRuntimeConfig(config) {
|
|||
}
|
||||
else {
|
||||
inputCopy.textContent = "Start with the ugly input.";
|
||||
runControlsCopy.textContent = "Pin what you know. Infer what you do not.";
|
||||
runControlsCopy.textContent = "Pin what you know. Search what you do not.";
|
||||
}
|
||||
}
|
||||
function resetRunArtifacts(options = {}) {
|
||||
|
|
@ -322,7 +321,7 @@ function resetRunArtifacts(options = {}) {
|
|||
}
|
||||
state.preprocessedImage = null;
|
||||
state.latticeImage = null;
|
||||
state.signalImage = null;
|
||||
state.guidanceImage = null;
|
||||
state.inference = null;
|
||||
state.inferenceMode = null;
|
||||
state.latticeSearch = null;
|
||||
|
|
@ -495,6 +494,9 @@ function renderInference() {
|
|||
<strong>Chosen Grid</strong>
|
||||
<span>${state.inference.target_width} x ${state.inference.target_height}</span>
|
||||
</div>
|
||||
<div class="info-card summary-card">
|
||||
<strong>Phase</strong>
|
||||
</div>
|
||||
<div class="info-card summary-card">
|
||||
<strong>Confidence</strong>
|
||||
<span>${formatNumber(state.inference.confidence, 3)}</span>
|
||||
|
|
@ -544,12 +546,7 @@ function renderStatusMetrics() {
|
|||
}
|
||||
else if (state.stageKey === "analysis" || state.stageKey === "selection") {
|
||||
if (state.inference) {
|
||||
const modeLabel = state.inferenceMode === "fixed"
|
||||
? "pinned"
|
||||
: state.inferenceMode === "autocorr"
|
||||
? "autocorr direct"
|
||||
: state.inferenceMode ?? "search";
|
||||
items.push({ label: "Mode", value: modeLabel });
|
||||
items.push({ label: "Mode", value: state.inferenceMode === "fixed" ? "pinned" : state.inferenceMode ?? "search" });
|
||||
items.push({ label: "Grid", value: `${state.inference.target_width} x ${state.inference.target_height}` });
|
||||
items.push({ label: "Confidence", value: formatNumber(state.inference.confidence, 3) });
|
||||
}
|
||||
|
|
@ -584,11 +581,11 @@ function renderStatusMetrics() {
|
|||
}
|
||||
else if (state.stageKey === "solver") {
|
||||
const solverTerms = [
|
||||
{ label: "Signal", value: formatNumber(frame?.terms.local_signal ?? frame?.solverMetrics.local_signal ?? null, 4) },
|
||||
{ label: "Grid align", value: formatNumber(frame?.terms.grid_alignment ?? frame?.solverMetrics.grid_alignment ?? null, 4) },
|
||||
{ label: "Smoothness", value: formatNumber(frame?.terms.smoothness ?? frame?.solverMetrics.smoothness ?? null, 4) },
|
||||
{ label: "Collapse", value: formatNumber(frame?.terms.collapse ?? frame?.solverMetrics.collapse ?? null, 4) },
|
||||
{ label: "Magnitude", value: formatNumber(frame?.terms.magnitude ?? frame?.solverMetrics.magnitude ?? null, 4) },
|
||||
{ label: "Coherence", value: formatNumber(frame?.terms.local_coherence ?? null, 4) },
|
||||
{ label: "Edge", value: formatNumber(frame?.terms.local_edge ?? null, 4) },
|
||||
{ label: "Smoothness", value: formatNumber(frame?.terms.smoothness ?? null, 4) },
|
||||
{ label: "Collapse", value: formatNumber(frame?.terms.collapse ?? null, 4) },
|
||||
{ label: "Magnitude", value: formatNumber(frame?.terms.magnitude ?? null, 4) },
|
||||
];
|
||||
items.push({
|
||||
label: "Grid",
|
||||
|
|
@ -623,23 +620,15 @@ function resolveViewerPanels() {
|
|||
leftAsset = state.latticeImage;
|
||||
leftLabel = "Lattice Prep";
|
||||
}
|
||||
if (state.signalImage) {
|
||||
leftAsset = state.signalImage;
|
||||
leftLabel = "Signal";
|
||||
if (state.guidanceImage) {
|
||||
leftAsset = state.guidanceImage;
|
||||
leftLabel = "Guidance";
|
||||
}
|
||||
if (frame) {
|
||||
if (state.stageKey === "solver" && state.signalImage) {
|
||||
leftAsset = state.signalImage;
|
||||
rightAsset = frame.samplingOverlayImage;
|
||||
leftLabel = "Signal";
|
||||
rightLabel = "Sampling Overlay";
|
||||
}
|
||||
else {
|
||||
leftAsset = frame.samplingOverlayImage;
|
||||
rightAsset = frame.outputImage;
|
||||
leftLabel = "Sampling Overlay";
|
||||
rightLabel = "Current Output";
|
||||
}
|
||||
leftAsset = frame.samplingOverlayImage;
|
||||
rightAsset = frame.outputImage;
|
||||
leftLabel = "Sampling Overlay";
|
||||
rightLabel = "Current Output";
|
||||
}
|
||||
else if (state.cleanupImage) {
|
||||
if (state.heatmapImage) {
|
||||
|
|
@ -794,14 +783,12 @@ function renderLossChart() {
|
|||
const domainMax = getLossDomainMax();
|
||||
const samples = buildLossSamples();
|
||||
const observedMaxLoss = samples.length === 0 ? null : Math.max(...samples.map((sample) => sample.loss));
|
||||
const observedMinLoss = samples.length === 0 ? null : Math.min(...samples.map((sample) => sample.loss));
|
||||
if (observedMaxLoss !== null && observedMinLoss !== null) {
|
||||
const observedAbsLoss = Math.max(Math.abs(observedMaxLoss), Math.abs(observedMinLoss));
|
||||
state.lossAxisMax = Math.max(state.lossAxisMax ?? 0, observedAbsLoss);
|
||||
if (observedMaxLoss !== null) {
|
||||
state.lossAxisMax = Math.max(state.lossAxisMax ?? 0, observedMaxLoss);
|
||||
}
|
||||
const axisAbsLoss = Math.max(0.001, state.lossAxisMax ?? Math.max(Math.abs(observedMaxLoss ?? 1), Math.abs(observedMinLoss ?? 0)));
|
||||
const maxLoss = axisAbsLoss * 1.05;
|
||||
const minLoss = -axisAbsLoss * 1.05;
|
||||
const axisMaxLoss = Math.max(0.001, state.lossAxisMax ?? observedMaxLoss ?? 1);
|
||||
const maxLoss = axisMaxLoss * 1.05;
|
||||
const minLoss = 0;
|
||||
const chartLeft = 88;
|
||||
const chartRight = width - 18;
|
||||
const chartTop = 16;
|
||||
|
|
@ -1240,7 +1227,7 @@ function buildFormData() {
|
|||
["seed", String(parseOptionalInteger(seedInput) ?? 7)],
|
||||
["device", runtimeConfig?.hostedDemo ? "cpu" : deviceInput.value],
|
||||
["strip_background", runtimeConfig?.hostedDemo ? "false" : stripBackgroundInput.checked ? "true" : "false"],
|
||||
["skip_candidate_rerank", skipRerankInput.checked ? "true" : "false"],
|
||||
["skip_candidate_rerank", "false"],
|
||||
];
|
||||
for (const [key, value] of values) {
|
||||
if (value !== null) {
|
||||
|
|
@ -1302,7 +1289,7 @@ async function handleEvent(eventName, payload) {
|
|||
state.preprocessedImage = payload.sourceImage;
|
||||
addLog("Preprocess", "Stripped edge-connected background noise.");
|
||||
break;
|
||||
case "lattice_inference_started":
|
||||
case "lattice_search_started":
|
||||
state.latticeSearch = {
|
||||
candidateCount: Number(payload.candidateCount ?? 0),
|
||||
completedCandidates: 0,
|
||||
|
|
@ -1311,10 +1298,10 @@ async function handleEvent(eventName, payload) {
|
|||
currentTargetHeight: null,
|
||||
bestScore: null,
|
||||
};
|
||||
setStage("inference", "Autocorr lattice", `Testing ${state.latticeSearch.candidateCount} autocorr candidate sizes from canonical cell centers.`);
|
||||
addLog("Inference", `Started autocorr lattice inference across ${String(payload.candidateCount)} size candidates.`);
|
||||
setStage("inference", "Lattice search", `Testing ${state.latticeSearch.candidateCount} candidate sizes from canonical cell centers.`);
|
||||
addLog("Search", `Started lattice search across ${String(payload.candidateCount)} size candidates.`);
|
||||
break;
|
||||
case "lattice_inference_progress":
|
||||
case "lattice_search_progress":
|
||||
if (!state.latticeSearch) {
|
||||
state.latticeSearch = {
|
||||
candidateCount: Number(payload.totalCandidates ?? 0),
|
||||
|
|
@ -1331,7 +1318,7 @@ async function handleEvent(eventName, payload) {
|
|||
state.latticeSearch.currentTargetHeight = Number(payload.targetHeight ?? 0);
|
||||
state.latticeSearch.bestScore =
|
||||
payload.bestScore === null || payload.bestScore === undefined ? state.latticeSearch.bestScore : Number(payload.bestScore);
|
||||
setStage("inference", "Autocorr lattice", `Size ${state.latticeSearch.completedCandidates} / ${state.latticeSearch.candidateCount}: scored ${state.latticeSearch.currentTargetWidth} x ${state.latticeSearch.currentTargetHeight} from canonical cell centers.`);
|
||||
setStage("inference", "Lattice search", `Size ${state.latticeSearch.completedCandidates} / ${state.latticeSearch.candidateCount}: scored ${state.latticeSearch.currentTargetWidth} x ${state.latticeSearch.currentTargetHeight} from canonical cell centers.`);
|
||||
break;
|
||||
case "inference_candidates_ready":
|
||||
state.inference = payload.inference;
|
||||
|
|
@ -1398,11 +1385,11 @@ async function handleEvent(eventName, payload) {
|
|||
addLog("Selection", "Committed to a lattice size.");
|
||||
break;
|
||||
case "analysis_completed":
|
||||
addLog("Analysis", "Rendered the diagnostic edge preview.");
|
||||
addLog("Scout", "Built the edge map that tells the solver where the floorboards creak.");
|
||||
break;
|
||||
case "phase_field_prepared":
|
||||
state.latticeImage = payload.latticeImage;
|
||||
state.signalImage = payload.signalImage;
|
||||
state.guidanceImage = payload.guidanceImage;
|
||||
state.phaseFieldPrep = {
|
||||
targetWidth: Number(payload.targetWidth ?? 0),
|
||||
targetHeight: Number(payload.targetHeight ?? 0),
|
||||
|
|
@ -1435,8 +1422,8 @@ async function handleEvent(eventName, payload) {
|
|||
case "cleanup_completed":
|
||||
state.cleanupImage = payload.cleanedImage;
|
||||
state.heatmapImage = payload.heatmapImage;
|
||||
setStage("cleanup", "Cleanup", "Snapping alpha to the final discrete mask.");
|
||||
addLog("Cleanup", "Applied alpha snapping.");
|
||||
setStage("cleanup", "Cleanup", "Sweeping isolated artifacts and smoothing the last stubborn junk.");
|
||||
addLog("Cleanup", "Ran the local cleanup sweep.");
|
||||
break;
|
||||
case "palette_completed":
|
||||
state.finalOutputImage = payload.outputImage;
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<form id="controlsForm" class="controls-grid">
|
||||
<label id="deviceField" class="field">
|
||||
<label id="deviceField" class="field" hidden>
|
||||
<span class="field-heading">
|
||||
<span class="field-label">Device</span>
|
||||
<span
|
||||
|
|
@ -263,7 +263,7 @@
|
|||
</span>
|
||||
<input id="seedInput" class="field-control" name="seed" type="number" min="0" value="7" />
|
||||
</label>
|
||||
<label id="stripBackgroundField" class="toggle-row ui-surface ui-frame">
|
||||
<label id="stripBackgroundField" class="toggle-row ui-surface ui-frame" hidden>
|
||||
<input id="stripBackgroundInput" name="stripBackground" type="checkbox" />
|
||||
<span class="toggle-copy">
|
||||
<span>Strip checkerboard-ish junk</span>
|
||||
|
|
@ -277,20 +277,6 @@
|
|||
>
|
||||
</span>
|
||||
</label>
|
||||
<label class="toggle-row ui-surface ui-frame">
|
||||
<input id="skipRerankInput" name="skipRerank" type="checkbox" />
|
||||
<span class="toggle-copy">
|
||||
<span>Skip candidate rerank</span>
|
||||
<span
|
||||
class="help-tip"
|
||||
tabindex="0"
|
||||
role="note"
|
||||
title="Skip the preview rerank pass that rescues shaky size picks when confidence is low. Faster, but more likely to keep a bad lattice guess."
|
||||
aria-label="Skip the preview rerank pass that rescues shaky size picks when confidence is low. Faster, but more likely to keep a bad lattice guess."
|
||||
>?</span
|
||||
>
|
||||
</span>
|
||||
</label>
|
||||
</form>
|
||||
<button id="runButton" class="button" type="button">Run The Machine</button>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -386,7 +386,7 @@ def test_gui_static_assets_disable_browser_caching() -> None:
|
|||
|
||||
app = create_app()
|
||||
html_status, html_headers, html_body = asyncio.run(_get_response(app, "/app/"))
|
||||
js_status, js_headers, _js_body = asyncio.run(_get_response(app, "/app/app.js"))
|
||||
js_status, js_headers, js_body = asyncio.run(_get_response(app, "/app/app.js"))
|
||||
|
||||
assert html_status == 200
|
||||
assert js_status == 200
|
||||
|
|
@ -395,8 +395,15 @@ def test_gui_static_assets_disable_browser_caching() -> None:
|
|||
assert html_headers["pragma"] == "no-cache"
|
||||
assert js_headers["pragma"] == "no-cache"
|
||||
html_text = html_body.decode("utf-8")
|
||||
js_text = js_body.decode("utf-8")
|
||||
assert "./styles.css?v=" in html_text
|
||||
assert "./app.js?v=" in html_text
|
||||
assert 'id="deviceField" class="field" hidden' in html_text
|
||||
assert 'id="stripBackgroundField" class="toggle-row ui-surface ui-frame" hidden' in html_text
|
||||
assert "skipRerankInput" not in html_text
|
||||
assert "Skip candidate rerank" not in html_text
|
||||
assert '["skip_candidate_rerank", "false"]' in js_text
|
||||
assert "skipRerankInput" not in js_text
|
||||
|
||||
|
||||
def test_gui_hosted_root_serves_landing_page(monkeypatch, tmp_path: Path) -> None:
|
||||
|
|
@ -711,7 +718,8 @@ def test_gui_hosted_config_endpoint_exposes_demo_limits(monkeypatch, tmp_path: P
|
|||
payload = _response_json(_route_endpoint(app, "/api/config", "GET")())
|
||||
|
||||
assert payload["hostedDemo"] is True
|
||||
assert payload["limits"]["maxOutputDimension"] == 256
|
||||
assert payload["limits"]["maxUploadBytes"] == 2 * 1_048_576
|
||||
assert payload["limits"]["maxOutputDimension"] == 512
|
||||
assert payload["limits"]["defaultSteps"] == 32
|
||||
assert payload["ui"]["showDeviceControl"] is False
|
||||
assert payload["ui"]["showStripBackgroundControl"] is False
|
||||
|
|
@ -943,15 +951,15 @@ def test_gui_heimdall_patreon_auth_start_sends_membership_policy(monkeypatch, tm
|
|||
}
|
||||
|
||||
|
||||
def test_hosted_job_options_use_autocorr_and_allow_rerank() -> None:
|
||||
def test_hosted_job_options_use_autocorr_and_force_rerank() -> None:
|
||||
from repixelizer.gui import HostedDemoConfig, _normalize_job_options
|
||||
|
||||
config = HostedDemoConfig(
|
||||
hosted_demo=True,
|
||||
show_queue_panel=True,
|
||||
max_upload_bytes=1_048_576,
|
||||
max_upload_bytes=2 * 1_048_576,
|
||||
max_input_dimension=2048,
|
||||
max_output_dimension=256,
|
||||
max_output_dimension=512,
|
||||
default_steps=32,
|
||||
max_steps=48,
|
||||
queue_capacity=10,
|
||||
|
|
@ -972,12 +980,12 @@ def test_hosted_job_options_use_autocorr_and_allow_rerank() -> None:
|
|||
seed=7,
|
||||
device="auto",
|
||||
strip_background=False,
|
||||
skip_candidate_rerank=False,
|
||||
skip_candidate_rerank=True,
|
||||
)
|
||||
|
||||
assert options["target_size"] is None
|
||||
assert options["lattice_inference_mode"] == "autocorr"
|
||||
assert options["max_inferred_target_size"] == 256
|
||||
assert options["max_inferred_target_size"] == 512
|
||||
assert options["skip_candidate_rerank"] is False
|
||||
|
||||
|
||||
|
|
@ -1153,7 +1161,7 @@ def test_gui_hosted_jobs_dispatch_direct_autocorr_pipeline(monkeypatch, tmp_path
|
|||
assert created.status_code == 200
|
||||
assert calls
|
||||
assert calls[0]["lattice_inference_mode"] == "autocorr"
|
||||
assert calls[0]["max_inferred_target_size"] == 256
|
||||
assert calls[0]["max_inferred_target_size"] == 512
|
||||
assert calls[0]["enable_candidate_rerank"] is True
|
||||
assert calls[0]["target_size"] is None
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue