feat(aurora-smart): add dual-stack orchestration with policy, audit, and UI toggle
This commit is contained in:
@@ -836,6 +836,35 @@
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<div style="margin-top:10px; border:1px solid rgba(255,255,255,0.08); border-radius:8px; padding:8px; background:var(--bg2);">
|
||||
<div class="aurora-note" style="margin-top:0;">Smart Orchestrator (Dual Stack)</div>
|
||||
<label class="aurora-checkline" style="margin-top:5px;">
|
||||
<input type="checkbox" id="auroraSmartEnabled" checked>
|
||||
Auto (Aurora + Kling when needed)
|
||||
</label>
|
||||
<div class="aurora-export-grid" style="margin-top:8px;">
|
||||
<label>Strategy
|
||||
<select id="auroraSmartStrategy">
|
||||
<option value="auto" selected>Auto</option>
|
||||
<option value="local_only">Local only</option>
|
||||
<option value="local_then_kling">Local then Kling</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>Budget
|
||||
<select id="auroraSmartBudget">
|
||||
<option value="low">Low</option>
|
||||
<option value="normal" selected>Normal</option>
|
||||
<option value="high">High</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<label class="aurora-checkline" style="margin-top:6px;">
|
||||
<input type="checkbox" id="auroraSmartPreferQuality" checked>
|
||||
Prefer quality over speed
|
||||
</label>
|
||||
<div id="auroraSmartHint" class="aurora-note">policy: standby</div>
|
||||
</div>
|
||||
|
||||
<div style="display:flex; gap:8px; margin-top:12px;">
|
||||
<button id="auroraAnalyzeBtn" class="btn btn-ghost" onclick="auroraAnalyze()" disabled>🔍 Аналіз</button>
|
||||
<button id="auroraAudioProcessBtn" class="btn btn-ghost" style="display:none;" onclick="auroraStartAudio()">🎧 Audio process</button>
|
||||
@@ -847,6 +876,8 @@
|
||||
<div class="aurora-card">
|
||||
<div class="aurora-title">Job Status</div>
|
||||
<div class="aurora-kv"><span class="k">Job ID</span><span class="v" id="auroraJobId">—</span></div>
|
||||
<div class="aurora-kv"><span class="k">Smart Run</span><span class="v" id="auroraSmartRunId">—</span></div>
|
||||
<div class="aurora-kv"><span class="k">Smart Policy</span><span class="v" id="auroraSmartPolicy">—</span></div>
|
||||
<div class="aurora-kv"><span class="k">Статус</span><span class="v" id="auroraJobStatus">idle</span></div>
|
||||
<div class="aurora-kv"><span class="k">Етап</span><span class="v" id="auroraJobStage">—</span></div>
|
||||
<div class="aurora-kv"><span class="k">Черга</span><span class="v" id="auroraQueuePos">—</span></div>
|
||||
@@ -2018,6 +2049,8 @@ let currentAudio = null;
|
||||
let auroraMode = 'tactical';
|
||||
let auroraSelectedFile = null;
|
||||
let auroraJobId = null;
|
||||
let auroraSmartRunId = null;
|
||||
let auroraSmartStatusCache = null;
|
||||
let auroraPollTimer = null;
|
||||
let auroraResultCache = null;
|
||||
let auroraAnalysisCache = null;
|
||||
@@ -2035,8 +2068,10 @@ let auroraChatBusy = false;
|
||||
let auroraFolderPath = null;
|
||||
const AURORA_MAX_TRANSIENT_ERRORS = 12;
|
||||
const AURORA_ACTIVE_JOB_KEY = 'aurora_active_job_id';
|
||||
const AURORA_SMART_RUN_KEY = 'aurora_smart_run_id';
|
||||
const AURORA_TIMING_CACHE_PREFIX = 'aurora_timing_cache_v1:';
|
||||
try { auroraJobId = localStorage.getItem(AURORA_ACTIVE_JOB_KEY) || null; } catch (_) {}
|
||||
try { auroraSmartRunId = localStorage.getItem(AURORA_SMART_RUN_KEY) || null; } catch (_) {}
|
||||
let mediaTabBootstrapped = false;
|
||||
let aistalkTabBootstrapped = false;
|
||||
let aistalkRunId = null;
|
||||
@@ -2214,6 +2249,38 @@ function auroraPersistActiveJob() {
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
function auroraPersistSmartRun() {
|
||||
try {
|
||||
if (auroraSmartRunId) localStorage.setItem(AURORA_SMART_RUN_KEY, auroraSmartRunId);
|
||||
else localStorage.removeItem(AURORA_SMART_RUN_KEY);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
function auroraSetSmartRunId(runId) {
|
||||
const normalized = String(runId || '').trim();
|
||||
auroraSmartRunId = normalized || null;
|
||||
const el = document.getElementById('auroraSmartRunId');
|
||||
if (el) el.textContent = auroraSmartRunId || '—';
|
||||
auroraPersistSmartRun();
|
||||
}
|
||||
|
||||
function auroraSetSmartPolicyText(text) {
|
||||
const el = document.getElementById('auroraSmartPolicy');
|
||||
const hint = document.getElementById('auroraSmartHint');
|
||||
const msg = String(text || '').trim() || '—';
|
||||
if (el) el.textContent = msg;
|
||||
if (hint) hint.textContent = `policy: ${msg}`;
|
||||
}
|
||||
|
||||
function auroraSmartConfig() {
|
||||
return {
|
||||
enabled: Boolean(document.getElementById('auroraSmartEnabled')?.checked),
|
||||
strategy: document.getElementById('auroraSmartStrategy')?.value || 'auto',
|
||||
budget_tier: document.getElementById('auroraSmartBudget')?.value || 'normal',
|
||||
prefer_quality: Boolean(document.getElementById('auroraSmartPreferQuality')?.checked),
|
||||
};
|
||||
}
|
||||
|
||||
function auroraTimingCacheKey(jobId) {
|
||||
const id = String(jobId || '').trim();
|
||||
return id ? `${AURORA_TIMING_CACHE_PREFIX}${id}` : '';
|
||||
@@ -2301,6 +2368,9 @@ function auroraSetSelectedFile(file) {
|
||||
auroraSuggestedPriority = 'balanced';
|
||||
auroraSuggestedExport = {};
|
||||
auroraPresetMode = 'balanced';
|
||||
auroraSetSmartRunId(null);
|
||||
auroraSmartStatusCache = null;
|
||||
auroraSetSmartPolicyText('standby');
|
||||
auroraResetAnalysisControls();
|
||||
auroraUpdateQueuePosition(null);
|
||||
auroraUpdateStorage(null);
|
||||
@@ -2474,6 +2544,9 @@ function auroraSelectJob(jobId) {
|
||||
const id = String(jobId || '').trim();
|
||||
if (!id) return;
|
||||
auroraSetActiveJobId(id);
|
||||
auroraSetSmartRunId(null);
|
||||
auroraSmartStatusCache = null;
|
||||
auroraSetSmartPolicyText('manual open');
|
||||
auroraStatusCache = null;
|
||||
auroraResultCache = null;
|
||||
auroraPollErrorCount = 0;
|
||||
@@ -2491,6 +2564,9 @@ function auroraSelectJob(jobId) {
|
||||
function auroraClearActiveJob() {
|
||||
auroraStopPolling();
|
||||
auroraSetActiveJobId(null);
|
||||
auroraSetSmartRunId(null);
|
||||
auroraSmartStatusCache = null;
|
||||
auroraSetSmartPolicyText('—');
|
||||
auroraStatusCache = null;
|
||||
auroraResultCache = null;
|
||||
auroraLastProgress = 0;
|
||||
@@ -3165,6 +3241,9 @@ async function auroraReprocess(options) {
|
||||
}
|
||||
const data = await r.json();
|
||||
auroraSetActiveJobId(data.job_id);
|
||||
auroraSetSmartRunId(null);
|
||||
auroraSmartStatusCache = null;
|
||||
auroraSetSmartPolicyText('audio local');
|
||||
auroraStatusCache = null;
|
||||
auroraResultCache = null;
|
||||
auroraPollErrorCount = 0;
|
||||
@@ -3301,6 +3380,9 @@ async function auroraStartAudio() {
|
||||
}
|
||||
const data = await r.json();
|
||||
auroraSetActiveJobId(data.job_id);
|
||||
auroraSetSmartRunId(null);
|
||||
auroraSmartStatusCache = null;
|
||||
auroraSetSmartPolicyText('reprocess local');
|
||||
auroraStatusCache = null;
|
||||
auroraResultCache = null;
|
||||
auroraPollErrorCount = 0;
|
||||
@@ -3331,6 +3413,29 @@ function auroraStopPolling() {
|
||||
auroraPollInFlight = false;
|
||||
}
|
||||
|
||||
async function auroraPollSmartStatus({ quiet = true } = {}) {
|
||||
if (!auroraSmartRunId) return null;
|
||||
try {
|
||||
const r = await fetch(`${API}/api/aurora/process-smart/${encodeURIComponent(auroraSmartRunId)}`);
|
||||
if (r.status === 404) {
|
||||
auroraSetSmartRunId(null);
|
||||
auroraSmartStatusCache = null;
|
||||
auroraSetSmartPolicyText('—');
|
||||
return null;
|
||||
}
|
||||
if (!r.ok) throw new Error(`HTTP ${r.status}`);
|
||||
const smart = await r.json();
|
||||
auroraSmartStatusCache = smart;
|
||||
const strategy = smart?.policy?.strategy || 'auto';
|
||||
const phase = smart?.phase || smart?.status || '—';
|
||||
auroraSetSmartPolicyText(`${strategy} · ${phase}`);
|
||||
return smart;
|
||||
} catch (e) {
|
||||
if (!quiet) console.warn('aurora smart status error:', e);
|
||||
return auroraSmartStatusCache;
|
||||
}
|
||||
}
|
||||
|
||||
async function auroraPollStatus() {
|
||||
if (!auroraJobId || auroraPollInFlight) return;
|
||||
auroraPollInFlight = true;
|
||||
@@ -3346,6 +3451,7 @@ async function auroraPollStatus() {
|
||||
}
|
||||
if (!r.ok) throw new Error(`HTTP ${r.status}`);
|
||||
const st = await r.json();
|
||||
const smart = await auroraPollSmartStatus({ quiet: true });
|
||||
auroraStatusCache = st;
|
||||
auroraPollErrorCount = 0;
|
||||
const cachedTiming = auroraGetPersistedTiming(auroraJobId) || {};
|
||||
@@ -3369,10 +3475,24 @@ async function auroraPollStatus() {
|
||||
const reBtn = document.getElementById('auroraReprocessBtn');
|
||||
if (reBtn) reBtn.disabled = !(st.status === 'completed' || st.status === 'failed' || st.status === 'cancelled');
|
||||
if (st.status === 'completed') {
|
||||
const smartActive = smart && !['completed', 'failed', 'cancelled'].includes(String(smart.status || '').toLowerCase());
|
||||
if (smartActive) {
|
||||
if (!auroraResultCache) {
|
||||
await auroraLoadResult(auroraJobId);
|
||||
}
|
||||
const kStat = smart?.kling?.status ? ` · Kling ${smart.kling.status}` : '';
|
||||
auroraSetProgress(99, 'processing', `smart orchestration (${smart.phase || 'running'}${kStat})`);
|
||||
const cancelBtn = document.getElementById('auroraCancelBtn');
|
||||
if (cancelBtn) cancelBtn.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
auroraStopPolling();
|
||||
await auroraLoadResult(auroraJobId);
|
||||
const cancelBtn = document.getElementById('auroraCancelBtn');
|
||||
if (cancelBtn) cancelBtn.style.display = 'none';
|
||||
if (smart && smart.selected_stack) {
|
||||
auroraChatAdd('assistant', `Smart run завершено. Selected stack: ${smart.selected_stack}.`);
|
||||
}
|
||||
await auroraRefreshJobs();
|
||||
} else if (st.status === 'failed' || st.status === 'cancelled') {
|
||||
auroraStopPolling();
|
||||
@@ -3406,20 +3526,29 @@ async function auroraStart() {
|
||||
return;
|
||||
}
|
||||
const analysisControls = auroraCollectAnalysisControls();
|
||||
const smartCfg = auroraSmartConfig();
|
||||
const fd = new FormData();
|
||||
fd.append('file', auroraSelectedFile);
|
||||
fd.append('mode', auroraMode);
|
||||
fd.append('priority', analysisControls.priority || auroraSuggestedPriority || 'balanced');
|
||||
const uiExport = auroraCollectExportOptions();
|
||||
const analysisExport = auroraBuildAnalysisExportHints(analysisControls);
|
||||
fd.append('export_options', JSON.stringify({ ...auroraSuggestedExport, ...uiExport, ...analysisExport }));
|
||||
const mergedExport = { ...auroraSuggestedExport, ...uiExport, ...analysisExport };
|
||||
fd.append('export_options', JSON.stringify(mergedExport));
|
||||
if (smartCfg.enabled) {
|
||||
fd.append('strategy', smartCfg.strategy || 'auto');
|
||||
fd.append('prefer_quality', smartCfg.prefer_quality ? 'true' : 'false');
|
||||
fd.append('budget_tier', smartCfg.budget_tier || 'normal');
|
||||
fd.append('learning_enabled', 'true');
|
||||
}
|
||||
_auroraLogLines = [];
|
||||
const startBtn = document.getElementById('auroraStartBtn');
|
||||
const quickStartBtn = document.getElementById('auroraStartFromAnalysisBtn');
|
||||
if (startBtn) startBtn.disabled = true;
|
||||
if (quickStartBtn) quickStartBtn.disabled = true;
|
||||
try {
|
||||
const r = await fetch(`${API}/api/aurora/upload`, {
|
||||
const endpoint = smartCfg.enabled ? '/api/aurora/process-smart' : '/api/aurora/upload';
|
||||
const r = await fetch(`${API}${endpoint}`, {
|
||||
method: 'POST',
|
||||
body: fd,
|
||||
});
|
||||
@@ -3428,7 +3557,23 @@ async function auroraStart() {
|
||||
throw new Error(body || `HTTP ${r.status}`);
|
||||
}
|
||||
const data = await r.json();
|
||||
auroraSetActiveJobId(data.job_id);
|
||||
const localJobId = data.local_job_id || data.job_id;
|
||||
if (!localJobId) {
|
||||
throw new Error('job_id missing in response');
|
||||
}
|
||||
auroraSetActiveJobId(localJobId);
|
||||
if (smartCfg.enabled) {
|
||||
auroraSetSmartRunId(data.smart_run_id || null);
|
||||
const policyStrategy = data?.policy?.strategy || smartCfg.strategy || 'auto';
|
||||
const policyScore = Number(data?.policy?.score);
|
||||
const scoreTxt = Number.isFinite(policyScore) ? ` (${policyScore.toFixed(2)})` : '';
|
||||
auroraSetSmartPolicyText(`${policyStrategy}${scoreTxt}`);
|
||||
auroraChatAdd('assistant', `Smart run ${data.smart_run_id || '—'}: strategy=${policyStrategy}`);
|
||||
} else {
|
||||
auroraSetSmartRunId(null);
|
||||
auroraSmartStatusCache = null;
|
||||
auroraSetSmartPolicyText('manual local');
|
||||
}
|
||||
auroraStatusCache = null;
|
||||
auroraResultCache = null;
|
||||
auroraPollErrorCount = 0;
|
||||
@@ -3441,7 +3586,7 @@ async function auroraStart() {
|
||||
document.getElementById('auroraResultCard').style.display = 'none';
|
||||
const reBtn = document.getElementById('auroraReprocessBtn');
|
||||
if (reBtn) reBtn.disabled = true;
|
||||
auroraSetProgress(1, 'processing', 'dispatching');
|
||||
auroraSetProgress(1, 'processing', smartCfg.enabled ? 'dispatching smart orchestration' : 'dispatching');
|
||||
const cancelBtn = document.getElementById('auroraCancelBtn');
|
||||
if (cancelBtn) cancelBtn.style.display = 'inline-block';
|
||||
auroraStopPolling();
|
||||
@@ -3449,7 +3594,7 @@ async function auroraStart() {
|
||||
await auroraPollStatus();
|
||||
await auroraRefreshJobs();
|
||||
} catch (e) {
|
||||
alert(`Aurora upload error: ${e.message || e}`);
|
||||
alert(`Aurora start error: ${e.message || e}`);
|
||||
auroraSetProgress(0, 'failed', 'upload_error');
|
||||
} finally {
|
||||
if (startBtn) startBtn.disabled = !auroraSelectedFile;
|
||||
@@ -3805,6 +3950,10 @@ function auroraInitTab() {
|
||||
auroraBindDropzone();
|
||||
auroraRefreshHealth();
|
||||
auroraUpdatePriorityLabel();
|
||||
auroraSetSmartRunId(auroraSmartRunId);
|
||||
if (!auroraSmartRunId) {
|
||||
auroraSetSmartPolicyText('standby');
|
||||
}
|
||||
const quickStartBtn = document.getElementById('auroraStartFromAnalysisBtn');
|
||||
if (quickStartBtn) quickStartBtn.disabled = !auroraSelectedFile;
|
||||
if (!auroraTabBootstrapped) {
|
||||
@@ -3827,6 +3976,15 @@ function auroraInitTab() {
|
||||
}
|
||||
auroraUpdateQueuePosition((auroraStatusCache || {}).queue_position || null);
|
||||
auroraUpdateStorage((auroraStatusCache || {}).storage || null);
|
||||
if (auroraSmartRunId) {
|
||||
auroraPollSmartStatus({ quiet: true }).then((smart) => {
|
||||
if (!smart || typeof smart !== 'object') return;
|
||||
const localJob = smart?.local?.job_id || null;
|
||||
if (!auroraJobId && localJob) {
|
||||
auroraSetActiveJobId(localJob);
|
||||
}
|
||||
}).catch(() => {});
|
||||
}
|
||||
auroraRefreshJobs();
|
||||
if (auroraJobId && !auroraPollTimer) {
|
||||
auroraSetProgress(Math.max(1, auroraLastProgress || 0), 'processing', 'restoring previous job...');
|
||||
|
||||
Reference in New Issue
Block a user