修复设计计算错误并优化分享

This commit is contained in:
Lihatoo 2026-07-26 03:00:54 +08:00
parent dd21623cb5
commit 30cdd05914
5 changed files with 916 additions and 28 deletions

View file

@ -1703,6 +1703,13 @@ A+B</textarea>
<option value="true" data-i18n="design_wobble_allow"></option>
</select>
</label>
<label>
<span data-i18n="design_fixed_target_policy"></span>
<select id="designFixedTargetPolicy">
<option value="exclude_from_optimization" data-i18n="design_fixed_target_exclude"></option>
<option value="include" data-i18n="design_fixed_target_include"></option>
</select>
</label>
</div>
<div class="toggle-row" id="designTabToggle">
<button class="secondary panel-toggle active" type="button" data-design-tab="targets" data-i18n="design_tab_targets"></button>
@ -2221,6 +2228,9 @@ A+B</textarea>
design_wobble_label: "Wobble mutations",
design_wobble_allow: "允许",
design_wobble_prohibit: "禁止",
design_fixed_target_policy: "固定目标策略",
design_fixed_target_exclude: "剥离出优化",
design_fixed_target_include: "参与优化",
design_tab_targets: "Target Tubes",
design_tab_hard: "Hard Constraints",
design_tab_soft: "Soft Constraints",
@ -2440,6 +2450,7 @@ A+B</textarea>
design_ensemble_defect: "Ensemble defect",
design_trials_used: "试验数",
design_seed: "最佳种子",
design_fixed_excluded: "固定目标剥离",
design_constraint: "约束",
design_definition: "输入定义",
design_sequence: "设计序列",
@ -2568,6 +2579,9 @@ A+B</textarea>
design_wobble_label: "Wobble Mutations",
design_wobble_allow: "Allow",
design_wobble_prohibit: "Prohibit",
design_fixed_target_policy: "Fixed Target Policy",
design_fixed_target_exclude: "Exclude from Optimization",
design_fixed_target_include: "Include in Optimization",
design_tab_targets: "Target Tubes",
design_tab_hard: "Hard Constraints",
design_tab_soft: "Soft Constraints",
@ -2787,6 +2801,7 @@ A+B</textarea>
design_ensemble_defect: "Ensemble Defect",
design_trials_used: "Trials",
design_seed: "Best Seed",
design_fixed_excluded: "Fixed Targets Excluded",
design_constraint: "Constraint",
design_definition: "Input Definition",
design_sequence: "Designed Sequence",
@ -3803,9 +3818,14 @@ A+B</textarea>
const result = entry?.result && typeof entry.result === "object" ? entry.result : null;
return {
id: String(entry.id || entry.job_id || `${Date.now()}-${index}-${Math.random().toString(16).slice(2)}`),
job_id: entry.job_id || null,
status: entry.status || (result ? "success" : "input_only"),
created_at: entry.created_at_iso || entry.created_at || new Date().toISOString(),
updated_at: entry.updated_at || null,
elapsed_seconds: entry.elapsed_seconds ?? null,
payload,
result,
error: entry.error || null,
result_summary: buildHistorySummary(payload, entry.result_summary || {}),
};
}
@ -4050,6 +4070,7 @@ A+B</textarea>
document.getElementById("designSeed").value = payload.design?.seed ?? 0;
document.getElementById("designWobble").value = String(payload.design?.wobble_mutations ?? false);
document.getElementById("designMaxTimeHours").value = ((payload.design?.max_time_seconds ?? 0) / 3600);
document.getElementById("designFixedTargetPolicy").value = payload.design?.fixed_target_policy ?? "exclude_from_optimization";
const payloadCompute = Array.isArray(payload.compute) ? payload.compute : [];
document.querySelectorAll(".check input").forEach((input) => {
input.checked = payloadCompute.includes(input.value);
@ -4094,28 +4115,50 @@ A+B</textarea>
} else {
lastPayload = payload;
lastResult = null;
renderEmptyState();
if (["queued", "running", "cancel_requested"].includes(item.status)) {
const label = item.status === "queued"
? t("status_queued")
: (item.status === "cancel_requested" ? t("status_canceling") : t("status_polling"));
results.innerHTML = `<div class="result-card"><h3>${escapeHtml(label)}</h3><p>${t("history_job_id")}: ${escapeHtml(item.job_id || item.id || "-")}</p></div>`;
setStatus(formatStatus(label));
} else {
renderEmptyState();
}
}
}
function sharedJobIsActive(item) {
return ["queued", "running", "cancel_requested"].includes(item?.status);
}
async function fetchSharedItem(shareId, { poll = false } = {}) {
const suffix = poll ? "?poll=1" : "";
const { response, data } = await fetchJsonOrThrow(`/api/shares/${encodeURIComponent(shareId)}${suffix}`);
if (!response.ok || data.status !== "success") {
throw new Error(data?.error || `Share request failed with HTTP ${response.status}`);
}
const item = normalizeHistoryEntry(data.share, 0);
if (!item) throw new Error("Shared record is invalid.");
return item;
}
async function loadSharedHistoryFromUrl() {
const pathMatch = window.location.pathname.match(/^\/share\/([A-Za-z0-9_-]{8,128})$/);
const shareId = pathMatch?.[1] || new URLSearchParams(window.location.search).get("share");
if (!shareId) return;
document.body.classList.add("share-mode");
try {
const { response, data } = await fetchJsonOrThrow(`/api/shares/${encodeURIComponent(shareId)}`);
if (!response.ok || data.status !== "success") {
throw new Error(data?.error || `Share request failed with HTTP ${response.status}`);
}
const item = normalizeHistoryEntry(data.share, 0);
if (!item) throw new Error("Shared record is invalid.");
item.error = data.share.error || null;
let item = await fetchSharedItem(shareId);
applyHistoryItem(item);
document.querySelectorAll(".control-panel input, .control-panel select, .control-panel textarea, .control-panel button").forEach((node) => {
if (!node.closest(".toolbar") && !node.matches("[data-design-tab]")) node.disabled = true;
});
setStatus(formatStatus(t("history_shared_loaded")));
while (sharedJobIsActive(item)) {
await new Promise((resolve) => setTimeout(resolve, 1500));
item = await fetchSharedItem(shareId, { poll: true });
applyHistoryItem(item);
}
} catch (error) {
setStatus(`${t("history_share_failed")}: ${error.message}`, true);
}
@ -4133,7 +4176,7 @@ A+B</textarea>
<div class="compact-actions">
<button class="secondary" data-history-load="${item.job_id}" type="button">${t("history_load")}</button>
<button class="secondary" data-history-export="${item.job_id}" type="button">${t("history_export_one")}</button>
${["success", "error", "input_only", "canceled"].includes(item.status) ? `<button class="secondary" data-history-share="${item.job_id}" type="button">${t("history_share")}</button>` : ""}
<button class="secondary" data-history-share="${item.job_id}" type="button">${t("history_share")}</button>
${["queued", "running", "cancel_requested"].includes(item.status) ? "" : `<button class="secondary" data-history-delete="${item.job_id}" type="button">${t("history_delete")}</button>`}
</div>
</div>
@ -4329,6 +4372,7 @@ A+B</textarea>
seed: Number(document.getElementById("designSeed").value),
wobble_mutations: document.getElementById("designWobble").value === "true",
max_time_seconds: Math.round(Number(document.getElementById("designMaxTimeHours").value || 0) * 3600),
fixed_target_policy: document.getElementById("designFixedTargetPolicy").value,
},
hard_constraints: getHardConstraints(),
soft_constraints: getSoftConstraints(),
@ -4519,6 +4563,7 @@ A+B</textarea>
function renderDesignSummaryCard(result) {
const stats = result.design?.stats || {};
const excludedFixedCount = result.design?.optimization?.excluded_fixed_targets?.length || 0;
return `
<div class="result-card">
<h3>${t("design_summary_card")}</h3>
@ -4526,6 +4571,7 @@ A+B</textarea>
<div><strong>${t("design_ensemble_defect")}</strong><span class="metric-value">${Number(result.design?.ensemble_defect || 0).toFixed(6)}</span></div>
<div><strong>${t("design_trials_used")}</strong><span class="metric-value">${result.options?.trials ?? 1}</span></div>
<div><strong>${t("design_seed")}</strong><span class="metric-value">${stats.seed ?? "-"}</span></div>
<div><strong>${t("design_fixed_excluded")}</strong><span class="metric-value">${excludedFixedCount}</span></div>
<div><strong>${t("summary_elapsed")}</strong><span class="metric-value">${stats.design_time ? `${Number(stats.design_time).toFixed(3)} s` : "-"}</span></div>
</div>
<div class="compact-actions" style="margin-top: 12px;">
@ -6130,6 +6176,7 @@ A+B</textarea>
document.getElementById("designSeed").value = example.design?.seed ?? 0;
document.getElementById("designWobble").value = String(example.design?.wobble_mutations ?? false);
document.getElementById("designMaxTimeHours").value = ((example.design?.max_time_seconds ?? 0) / 3600);
document.getElementById("designFixedTargetPolicy").value = example.design?.fixed_target_policy ?? "exclude_from_optimization";
document.getElementById("complexesText").value = example.complexes_text;
const exampleCompute = Array.isArray(example.compute) ? example.compute : [];
document.querySelectorAll(".check input").forEach((input) => {