Fix HF dataset import internal error and training SQLite readiness.

Wrap AssistentImportHfDataset in try/catch, batch inserts in a transaction, clearer HF rows errors for gated datasets, EnsureTrainingReady with actionable message, and friendlier training UI errors instead of generic internal error.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-23 20:11:18 +03:00
co-authored by Cursor
parent b0f2736f65
commit 63538fe738
9 changed files with 323 additions and 155 deletions
+33 -15
View File
@@ -50,7 +50,7 @@ export function attachTraining(SA) {
setAgentHeardStats(state.agentLinked);
} catch (e) {
const msg = String(e.message || e);
setTrainStatus(isSqliteError(msg) ? `${msg}${sqliteHint()}` : msg);
setTrainStatus(formatTrainError(msg));
console.warn('loadAgentHeardSettings', e);
}
}
@@ -66,7 +66,7 @@ export function attachTraining(SA) {
state.agentSettings = data?.settings || settings;
setTrainStatus('Настройки «услышанного» сохранены');
} catch (e) {
setTrainStatus(String(e.message || e));
setTrainStatus(formatTrainError(e.message || e));
}
}
@@ -80,7 +80,7 @@ export function attachTraining(SA) {
setTrainStatus(`Подключено: +${data?.linked_now ?? 0}, всего ${data?.total_linked ?? '—'}`);
await refreshSamples();
} catch (e) {
setTrainStatus(String(e.message || e));
setTrainStatus(formatTrainError(e.message || e));
}
}
@@ -91,7 +91,25 @@ export function attachTraining(SA) {
function isSqliteError(msg) {
const s = String(msg || '').toLowerCase();
return s.includes('sqlite') || s.includes('sqlconnection');
return s.includes('sqlite')
|| s.includes('sqlconnection')
|| s.includes('train_samples')
|| s.includes('training database unavailable');
}
function isGenericServerError(msg) {
return /internal error occurred/i.test(String(msg || ''));
}
function formatTrainError(msg) {
const s = String(msg || '');
if (isSqliteError(s)) {
return `${s}${sqliteHint()}`;
}
if (isGenericServerError(s)) {
return `${s} — часто SQLite/импорт HF: gpu-rent seed-extensions, restart SwarmUI, HF token для gated.`;
}
return s;
}
function sqliteHint() {
@@ -167,7 +185,7 @@ export function attachTraining(SA) {
const data = await SA.request('AssistentListTrainSamples', { status, persona, limit: 300 });
if (data?.error) {
const msg = data.error;
setTrainStatus(isSqliteError(msg) ? `${msg}${sqliteHint()}` : msg);
setTrainStatus(formatTrainError(msg));
const stats = $('sa_train_stats');
if (stats) stats.textContent = 'Датасет недоступен (SQLite)';
state.samples = [];
@@ -196,7 +214,7 @@ export function attachTraining(SA) {
}
renderSamples();
} catch (e) {
setTrainStatus(String(e.message || e));
setTrainStatus(formatTrainError(e.message || e));
}
}
@@ -441,7 +459,7 @@ export function attachTraining(SA) {
renderHfList();
setTrainStatus(`Найдено: ${state.hfResults.length}`);
} catch (e) {
setTrainStatus(String(e.message || e));
setTrainStatus(formatTrainError(e.message || e));
}
}
@@ -522,7 +540,7 @@ export function attachTraining(SA) {
$('sa_train_samples')?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
} catch (e) {
const err = String(e.message || e);
const show = isSqliteError(err) ? `${err}${sqliteHint()}` : err;
const show = formatTrainError(err);
setTrainStatus(show);
if (hfSt) hfSt.textContent = show;
} finally {
@@ -578,7 +596,7 @@ export function attachTraining(SA) {
setTrainStatus(`Готово: ${data.name}`);
SA.app?.refreshModels?.();
} catch (e) {
setTrainStatus(String(e.message || e));
setTrainStatus(formatTrainError(e.message || e));
} finally {
btn?.removeAttribute('disabled');
}
@@ -712,7 +730,7 @@ export function attachTraining(SA) {
setTrainingTab('train');
setTrainStatus('QLoRA запущена — прогресс ниже');
} catch (e) {
setTrainStatus(String(e.message || e));
setTrainStatus(formatTrainError(e.message || e));
}
}
@@ -722,7 +740,7 @@ export function attachTraining(SA) {
setTrainingLock(false);
setTrainStatus('Отменено');
} catch (e) {
setTrainStatus(String(e.message || e));
setTrainStatus(formatTrainError(e.message || e));
}
}
@@ -760,7 +778,7 @@ export function attachTraining(SA) {
});
setTrainStatus('Раннер сохранён');
} catch (e) {
setTrainStatus(String(e.message || e));
setTrainStatus(formatTrainError(e.message || e));
}
}
@@ -805,7 +823,7 @@ export function attachTraining(SA) {
const data = await SA.request('AssistentImportDataset', { format: 'auto', content: text });
setTrainStatus(`Импорт: ${data.imported}`);
await refreshSamples();
} catch (err) { setTrainStatus(String(err.message || err)); }
} catch (err) { setTrainStatus(formatTrainError(err.message || err)); }
e.target.value = '';
});
$('sa_btn_train_export')?.addEventListener('click', async () => {
@@ -842,7 +860,7 @@ export function attachTraining(SA) {
setAgentHeardStats(state.agentLinked);
setTrainStatus('Пример подключён к агенту');
await refreshSamples();
} catch (err) { setTrainStatus(String(err.message || err)); }
} catch (err) { setTrainStatus(formatTrainError(err.message || err)); }
} else if (e.target.closest('[data-unlink]')) {
try {
const data = await SA.request('AssistentUnlinkTrainSampleFromAgent', { id });
@@ -850,7 +868,7 @@ export function attachTraining(SA) {
setAgentHeardStats(state.agentLinked);
setTrainStatus('Пример отключён от агента');
await refreshSamples();
} catch (err) { setTrainStatus(String(err.message || err)); }
} catch (err) { setTrainStatus(formatTrainError(err.message || err)); }
} else if (e.target.closest('[data-del]')) {
if (window.confirm('Удалить пример?')) {
await SA.request('AssistentDeleteTrainSample', { id });