Add adaptive learning and model rollback
This commit is contained in:
@@ -903,6 +903,11 @@ async function showActuator(actuatorId, evaluationMessage = "") {
|
||||
const knowledge = record.behavior.knowledge || [];
|
||||
const assumptions = record.behavior.assumptions || [];
|
||||
const uncertainties = record.behavior.uncertainties || [];
|
||||
const snapshots = record.behavior.model_snapshots || [];
|
||||
const activeModelVersion = record.behavior.active_model_version || "";
|
||||
const adaptiveUpdates = record.behavior.adaptive_weight_updates || [];
|
||||
const automationConflicts = record.behavior.automation_conflicts || [];
|
||||
const timeProfiles = record.behavior.time_profiles || [];
|
||||
const safetyControls = `
|
||||
<details class="manual-context" open>
|
||||
<summary>Sicherheit und manuelles Gegensteuern</summary>
|
||||
@@ -962,6 +967,43 @@ async function showActuator(actuatorId, evaluationMessage = "") {
|
||||
</div>
|
||||
</details>
|
||||
`;
|
||||
const adaptivePanel = `
|
||||
<details class="manual-context">
|
||||
<summary>v1.2 Lernen, Rollback und Konflikte</summary>
|
||||
<h3>Zeitprofile</h3>
|
||||
<div class="metric-grid">
|
||||
${timeProfiles.length ? timeProfiles.map(profile => `
|
||||
<div class="metric">
|
||||
<strong>${escapeHtml(profile.label)}</strong>
|
||||
${escapeHtml(profile.sample_count)} Samples · ${escapeHtml(profile.dominant_state || "offen")}
|
||||
<p class="muted">${Math.round((profile.confidence || 0) * 100)} % Profilklarheit</p>
|
||||
</div>
|
||||
`).join("") : "<div class='metric'><strong>Zeitprofile</strong>Noch keine Daten</div>"}
|
||||
</div>
|
||||
<h3>Modell-Snapshots</h3>
|
||||
<div class="decision-list">
|
||||
${snapshots.length ? snapshots.slice(-5).reverse().map(snapshot => `
|
||||
<div class="decision-row">
|
||||
<header>
|
||||
<strong>${escapeHtml(snapshot.version_id)}</strong>
|
||||
<span class="chip">${snapshot.version_id === activeModelVersion ? "aktiv" : "Rollback möglich"}</span>
|
||||
</header>
|
||||
<p class="muted">${escapeHtml(snapshot.sample_count)} Samples · ${escapeHtml(snapshot.high_confidence_sample_count)} eindeutig · Ø ${Math.round((snapshot.average_confidence || 0) * 100)} %</p>
|
||||
<p>${escapeHtml(snapshot.reason || "Kein Kommentar")}</p>
|
||||
${snapshot.version_id !== activeModelVersion ? `<button class="secondary compact" onclick="rollbackModel('${escapeHtml(record.actuator_entity_id)}', '${escapeHtml(snapshot.version_id)}')">Rollback</button>` : ""}
|
||||
</div>
|
||||
`).join("") : "<p class='muted'>Noch kein Modell-Snapshot gespeichert.</p>"}
|
||||
</div>
|
||||
<h3>Automatische Gewichtsanpassungen</h3>
|
||||
<ul>${adaptiveUpdates.length ? adaptiveUpdates.slice(-8).reverse().map(update => `
|
||||
<li><code>${escapeHtml(update.entity_id)}</code>: ${Math.round(update.previous_weight * 100)} % → ${Math.round(update.new_weight * 100)} %. ${escapeHtml(update.reason)}</li>
|
||||
`).join("") : "<li>Noch keine automatische Gewichtsanpassung.</li>"}</ul>
|
||||
<h3>Automation-Konflikte</h3>
|
||||
<ul>${automationConflicts.length ? automationConflicts.map(conflict => `
|
||||
<li><code>${escapeHtml(conflict.automation_entity_id)}</code>: <span class="${conflict.severity === "warning" ? "warn" : "muted"}">${escapeHtml(conflict.status)}</span> ${escapeHtml(conflict.reason)}</li>
|
||||
`).join("") : "<li>Keine aktiven Automation-Konflikte erkannt.</li>"}</ul>
|
||||
</details>
|
||||
`;
|
||||
const learnedAutomationActions = record.behavior.patterns.filter(
|
||||
pattern => pattern.source === "automation",
|
||||
).length;
|
||||
@@ -1073,6 +1115,7 @@ async function showActuator(actuatorId, evaluationMessage = "") {
|
||||
</div>
|
||||
${safetyControls}
|
||||
${decisionArchive}
|
||||
${adaptivePanel}
|
||||
<h3>Passende Home-Assistant-Automationen</h3>
|
||||
<p class="muted">Bei einer Übernahme pausiert SillyHome diese Automationen. Beim Stoppen können sie gezielt fortgesetzt werden.</p>
|
||||
<button class="secondary compact" onclick="refreshRelatedAutomations('${escapeHtml(record.actuator_entity_id)}')">Automationen neu suchen</button>
|
||||
@@ -1305,6 +1348,21 @@ async function saveSafetyProfile(actuatorId) {
|
||||
}
|
||||
}
|
||||
|
||||
async function rollbackModel(actuatorId, versionId) {
|
||||
if (!confirm(`${actuatorId}: wirklich auf Modell ${versionId} zurückrollen?`)) return;
|
||||
try {
|
||||
await api(`v1/actuators/${encodeURIComponent(actuatorId)}/model/rollback`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({version_id: versionId}),
|
||||
});
|
||||
invalidateDashboardCache();
|
||||
await loadConfiguredActuators();
|
||||
await showActuator(actuatorId, `Rollback auf ${versionId} ausgeführt.`);
|
||||
} catch (error) {
|
||||
alert(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function setActivation(actuatorId, active, pauseMatchingAutomations, restorePausedAutomations) {
|
||||
const question = active
|
||||
? pauseMatchingAutomations
|
||||
|
||||
Reference in New Issue
Block a user