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:
+25
-12
@@ -10708,7 +10708,7 @@ ${HELP_TEXT}`);
|
|||||||
setAgentHeardStats(state.agentLinked);
|
setAgentHeardStats(state.agentLinked);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const msg = String(e.message || e);
|
const msg = String(e.message || e);
|
||||||
setTrainStatus(isSqliteError(msg) ? `${msg} \u2014 ${sqliteHint()}` : msg);
|
setTrainStatus(formatTrainError(msg));
|
||||||
console.warn("loadAgentHeardSettings", e);
|
console.warn("loadAgentHeardSettings", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10723,7 +10723,7 @@ ${HELP_TEXT}`);
|
|||||||
state.agentSettings = data?.settings || settings;
|
state.agentSettings = data?.settings || settings;
|
||||||
setTrainStatus("\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \xAB\u0443\u0441\u043B\u044B\u0448\u0430\u043D\u043D\u043E\u0433\u043E\xBB \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u044B");
|
setTrainStatus("\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \xAB\u0443\u0441\u043B\u044B\u0448\u0430\u043D\u043D\u043E\u0433\u043E\xBB \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u044B");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setTrainStatus(String(e.message || e));
|
setTrainStatus(formatTrainError(e.message || e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function syncAllToAgent() {
|
async function syncAllToAgent() {
|
||||||
@@ -10736,7 +10736,7 @@ ${HELP_TEXT}`);
|
|||||||
setTrainStatus(`\u041F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u043E: +${data?.linked_now ?? 0}, \u0432\u0441\u0435\u0433\u043E ${data?.total_linked ?? "\u2014"}`);
|
setTrainStatus(`\u041F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u043E: +${data?.linked_now ?? 0}, \u0432\u0441\u0435\u0433\u043E ${data?.total_linked ?? "\u2014"}`);
|
||||||
await refreshSamples();
|
await refreshSamples();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setTrainStatus(String(e.message || e));
|
setTrainStatus(formatTrainError(e.message || e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function setTrainStatus(msg) {
|
function setTrainStatus(msg) {
|
||||||
@@ -10745,7 +10745,20 @@ ${HELP_TEXT}`);
|
|||||||
}
|
}
|
||||||
function isSqliteError(msg) {
|
function isSqliteError(msg) {
|
||||||
const s = String(msg || "").toLowerCase();
|
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} \u2014 ${sqliteHint()}`;
|
||||||
|
}
|
||||||
|
if (isGenericServerError(s)) {
|
||||||
|
return `${s} \u2014 \u0447\u0430\u0441\u0442\u043E SQLite/\u0438\u043C\u043F\u043E\u0440\u0442 HF: gpu-rent seed-extensions, restart SwarmUI, HF token \u0434\u043B\u044F gated.`;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
}
|
}
|
||||||
function sqliteHint() {
|
function sqliteHint() {
|
||||||
return "\u0411\u0430\u0437\u0430 Assistent (SQLite) \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0430 \u2014 gpu-rent seed-extensions + restart SwarmUI (\u22650.15.6).";
|
return "\u0411\u0430\u0437\u0430 Assistent (SQLite) \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0430 \u2014 gpu-rent seed-extensions + restart SwarmUI (\u22650.15.6).";
|
||||||
@@ -10816,7 +10829,7 @@ ${HELP_TEXT}`);
|
|||||||
const data = await SA2.request("AssistentListTrainSamples", { status, persona, limit: 300 });
|
const data = await SA2.request("AssistentListTrainSamples", { status, persona, limit: 300 });
|
||||||
if (data?.error) {
|
if (data?.error) {
|
||||||
const msg = data.error;
|
const msg = data.error;
|
||||||
setTrainStatus(isSqliteError(msg) ? `${msg} \u2014 ${sqliteHint()}` : msg);
|
setTrainStatus(formatTrainError(msg));
|
||||||
const stats2 = $("sa_train_stats");
|
const stats2 = $("sa_train_stats");
|
||||||
if (stats2) stats2.textContent = "\u0414\u0430\u0442\u0430\u0441\u0435\u0442 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D (SQLite)";
|
if (stats2) stats2.textContent = "\u0414\u0430\u0442\u0430\u0441\u0435\u0442 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D (SQLite)";
|
||||||
state.samples = [];
|
state.samples = [];
|
||||||
@@ -10845,7 +10858,7 @@ ${HELP_TEXT}`);
|
|||||||
}
|
}
|
||||||
renderSamples();
|
renderSamples();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setTrainStatus(String(e.message || e));
|
setTrainStatus(formatTrainError(e.message || e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function renderSamples() {
|
function renderSamples() {
|
||||||
@@ -11073,7 +11086,7 @@ ${HELP_TEXT}`);
|
|||||||
renderHfList();
|
renderHfList();
|
||||||
setTrainStatus(`\u041D\u0430\u0439\u0434\u0435\u043D\u043E: ${state.hfResults.length}`);
|
setTrainStatus(`\u041D\u0430\u0439\u0434\u0435\u043D\u043E: ${state.hfResults.length}`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setTrainStatus(String(e.message || e));
|
setTrainStatus(formatTrainError(e.message || e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function checkHfLink() {
|
async function checkHfLink() {
|
||||||
@@ -11148,7 +11161,7 @@ ${HELP_TEXT}`);
|
|||||||
$("sa_train_samples")?.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
$("sa_train_samples")?.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const err = String(e.message || e);
|
const err = String(e.message || e);
|
||||||
const show = isSqliteError(err) ? `${err} \u2014 ${sqliteHint()}` : err;
|
const show = formatTrainError(err);
|
||||||
setTrainStatus(show);
|
setTrainStatus(show);
|
||||||
if (hfSt) hfSt.textContent = show;
|
if (hfSt) hfSt.textContent = show;
|
||||||
} finally {
|
} finally {
|
||||||
@@ -11203,7 +11216,7 @@ ${HELP_TEXT}`);
|
|||||||
setTrainStatus(`\u0413\u043E\u0442\u043E\u0432\u043E: ${data.name}`);
|
setTrainStatus(`\u0413\u043E\u0442\u043E\u0432\u043E: ${data.name}`);
|
||||||
SA2.app?.refreshModels?.();
|
SA2.app?.refreshModels?.();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setTrainStatus(String(e.message || e));
|
setTrainStatus(formatTrainError(e.message || e));
|
||||||
} finally {
|
} finally {
|
||||||
btn?.removeAttribute("disabled");
|
btn?.removeAttribute("disabled");
|
||||||
}
|
}
|
||||||
@@ -11328,7 +11341,7 @@ ${HELP_TEXT}`);
|
|||||||
setTrainingTab("train");
|
setTrainingTab("train");
|
||||||
setTrainStatus("QLoRA \u0437\u0430\u043F\u0443\u0449\u0435\u043D\u0430 \u2014 \u043F\u0440\u043E\u0433\u0440\u0435\u0441\u0441 \u043D\u0438\u0436\u0435");
|
setTrainStatus("QLoRA \u0437\u0430\u043F\u0443\u0449\u0435\u043D\u0430 \u2014 \u043F\u0440\u043E\u0433\u0440\u0435\u0441\u0441 \u043D\u0438\u0436\u0435");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setTrainStatus(String(e.message || e));
|
setTrainStatus(formatTrainError(e.message || e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function cancelQlora() {
|
async function cancelQlora() {
|
||||||
@@ -11337,7 +11350,7 @@ ${HELP_TEXT}`);
|
|||||||
setTrainingLock(false);
|
setTrainingLock(false);
|
||||||
setTrainStatus("\u041E\u0442\u043C\u0435\u043D\u0435\u043D\u043E");
|
setTrainStatus("\u041E\u0442\u043C\u0435\u043D\u0435\u043D\u043E");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setTrainStatus(String(e.message || e));
|
setTrainStatus(formatTrainError(e.message || e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function refreshTrainModels() {
|
async function refreshTrainModels() {
|
||||||
@@ -11371,7 +11384,7 @@ ${HELP_TEXT}`);
|
|||||||
});
|
});
|
||||||
setTrainStatus("\u0420\u0430\u043D\u043D\u0435\u0440 \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D");
|
setTrainStatus("\u0420\u0430\u043D\u043D\u0435\u0440 \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setTrainStatus(String(e.message || e));
|
setTrainStatus(formatTrainError(e.message || e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function loadRunner() {
|
async function loadRunner() {
|
||||||
|
|||||||
+121
-72
@@ -426,94 +426,143 @@ public partial class SwarmAssistentExtension
|
|||||||
|
|
||||||
public async Task<JObject> AssistentImportHfDataset(Session session, JObject raw)
|
public async Task<JObject> AssistentImportHfDataset(Session session, JObject raw)
|
||||||
{
|
{
|
||||||
string dataset = raw?["dataset"]?.ToString();
|
try
|
||||||
int limit = raw?["limit"]?.Value<int?>() ?? 200;
|
|
||||||
JObject mapping = raw?["mapping"] as JObject;
|
|
||||||
string id = NormalizeHfDatasetId(dataset);
|
|
||||||
if (id is null)
|
|
||||||
{
|
{
|
||||||
return new JObject { ["error"] = "invalid dataset id" };
|
string dataset = raw?["dataset"]?.ToString();
|
||||||
}
|
int limit = raw?["limit"]?.Value<int?>() ?? 200;
|
||||||
JObject check = await CheckHfDatasetInternal(session, id, useCache: true);
|
JObject mapping = raw?["mapping"] as JObject;
|
||||||
string gate = check["gate"]?.ToString();
|
string id = NormalizeHfDatasetId(dataset);
|
||||||
if (gate == "rejected")
|
if (id is null)
|
||||||
{
|
|
||||||
return new JObject { ["error"] = check["reason"]?.ToString() ?? "rejected" };
|
|
||||||
}
|
|
||||||
if (MappingRequired(check, mapping))
|
|
||||||
{
|
|
||||||
return new JObject { ["error"] = "Нужен маппинг колонок", ["check"] = check };
|
|
||||||
}
|
|
||||||
mapping = ResolveHfMapping(check, mapping);
|
|
||||||
int take = Math.Clamp(limit, 1, 5000);
|
|
||||||
JArray rows = [];
|
|
||||||
string config = check["config"]?.ToString() ?? "default";
|
|
||||||
string split = check["split"]?.ToString() ?? "train";
|
|
||||||
int offset = 0;
|
|
||||||
while (rows.Count < take)
|
|
||||||
{
|
|
||||||
int chunk = Math.Min(100, take - rows.Count);
|
|
||||||
using HttpRequestMessage rowsReq = HfRequest($"{HfDatasetsServer}/rows?dataset={Uri.EscapeDataString(id)}&config={Uri.EscapeDataString(config)}&split={Uri.EscapeDataString(split)}&offset={offset}&length={chunk}", session);
|
|
||||||
using HttpResponseMessage rowsResp = await HttpClient.SendAsync(rowsReq);
|
|
||||||
string rowsBody = await rowsResp.Content.ReadAsStringAsync();
|
|
||||||
if (!rowsResp.IsSuccessStatusCode)
|
|
||||||
{
|
{
|
||||||
break;
|
return new JObject { ["error"] = "invalid dataset id" };
|
||||||
}
|
}
|
||||||
JObject parsed = JObject.Parse(rowsBody);
|
JObject check = await CheckHfDatasetInternal(session, id, useCache: true);
|
||||||
JArray batch = parsed["rows"] as JArray ?? [];
|
string gate = check["gate"]?.ToString();
|
||||||
if (batch.Count == 0)
|
if (gate == "rejected")
|
||||||
{
|
{
|
||||||
break;
|
return new JObject { ["error"] = check["reason"]?.ToString() ?? "rejected" };
|
||||||
}
|
}
|
||||||
foreach (JToken t in batch)
|
if (MappingRequired(check, mapping))
|
||||||
{
|
{
|
||||||
rows.Add(t);
|
return new JObject { ["error"] = "Нужен маппинг колонок", ["check"] = check };
|
||||||
}
|
}
|
||||||
offset += batch.Count;
|
mapping = ResolveHfMapping(check, mapping);
|
||||||
if (batch.Count < chunk)
|
int take = Math.Clamp(limit, 1, 5000);
|
||||||
|
JArray rows = [];
|
||||||
|
string config = check["config"]?.ToString() ?? "default";
|
||||||
|
string split = check["split"]?.ToString() ?? "train";
|
||||||
|
int offset = 0;
|
||||||
|
while (rows.Count < take)
|
||||||
{
|
{
|
||||||
break;
|
int chunk = Math.Min(100, take - rows.Count);
|
||||||
|
using HttpRequestMessage rowsReq = HfRequest($"{HfDatasetsServer}/rows?dataset={Uri.EscapeDataString(id)}&config={Uri.EscapeDataString(config)}&split={Uri.EscapeDataString(split)}&offset={offset}&length={chunk}", session);
|
||||||
|
using HttpResponseMessage rowsResp = await HttpClient.SendAsync(rowsReq);
|
||||||
|
string rowsBody = await rowsResp.Content.ReadAsStringAsync();
|
||||||
|
if (!rowsResp.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
if (rows.Count == 0)
|
||||||
|
{
|
||||||
|
return new JObject
|
||||||
|
{
|
||||||
|
["error"] = $"HF rows HTTP {(int)rowsResp.StatusCode}: {Clip(rowsBody, 240)}. "
|
||||||
|
+ "Для gated/NSFW добавь huggingface_api в User Settings.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
JObject parsed = JObject.Parse(rowsBody);
|
||||||
|
JArray batch = parsed["rows"] as JArray ?? [];
|
||||||
|
if (batch.Count == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
foreach (JToken t in batch)
|
||||||
|
{
|
||||||
|
rows.Add(t);
|
||||||
|
}
|
||||||
|
offset += batch.Count;
|
||||||
|
if (batch.Count < chunk)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if (rows.Count == 0)
|
||||||
if (rows.Count == 0)
|
|
||||||
{
|
|
||||||
rows = check["sample_rows"] as JArray ?? [];
|
|
||||||
}
|
|
||||||
int imported = 0;
|
|
||||||
foreach (JToken rowTok in rows.Take(take))
|
|
||||||
{
|
|
||||||
if (rowTok is not JObject row)
|
|
||||||
{
|
{
|
||||||
continue;
|
rows = check["sample_rows"] as JArray ?? [];
|
||||||
}
|
}
|
||||||
JObject rowData = row["row"] as JObject ?? row;
|
List<JObject> toSave = [];
|
||||||
JArray messages = ConvertHfRowToMessages(rowData, check["schema"] as JObject, mapping);
|
foreach (JToken rowTok in rows.Take(take))
|
||||||
if (messages is null || messages.Count == 0)
|
|
||||||
{
|
{
|
||||||
continue;
|
if (rowTok is not JObject row)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
JObject rowData = row["row"] as JObject ?? row;
|
||||||
|
JArray messages = ConvertHfRowToMessages(rowData, check["schema"] as JObject, mapping);
|
||||||
|
if (messages is null || messages.Count == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
toSave.Add(new JObject
|
||||||
|
{
|
||||||
|
["source"] = "hf",
|
||||||
|
["hf_repo"] = id,
|
||||||
|
["messages"] = messages,
|
||||||
|
["status"] = "draft",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
Memory.UpsertTrainSample(new JObject
|
int imported = toSave.Count > 0 ? Memory.ImportTrainSamplesBatch(toSave) : 0;
|
||||||
|
if (imported == 0 && check["runner_only"]?.Value<bool?>() == true)
|
||||||
{
|
{
|
||||||
["source"] = "hf",
|
return new JObject { ["success"] = true, ["imported"] = 0, ["runner_only"] = true, ["id"] = id, ["note"] = "Большой набор — используй HF id в QLoRA-раннере" };
|
||||||
["hf_repo"] = id,
|
}
|
||||||
["messages"] = messages,
|
if (imported == 0)
|
||||||
|
{
|
||||||
|
return new JObject
|
||||||
|
{
|
||||||
|
["error"] = rows.Count == 0
|
||||||
|
? "HF не отдал строки — проверь token (gated/NSFW) и маппинг колонок"
|
||||||
|
: "0 строк после маппинга — проверь колонки user/assistant",
|
||||||
|
["rows_fetched"] = rows.Count,
|
||||||
|
["id"] = id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return new JObject
|
||||||
|
{
|
||||||
|
["success"] = true,
|
||||||
|
["imported"] = imported,
|
||||||
|
["id"] = id,
|
||||||
|
["rows_fetched"] = rows.Count,
|
||||||
["status"] = "draft",
|
["status"] = "draft",
|
||||||
});
|
};
|
||||||
imported++;
|
|
||||||
}
|
}
|
||||||
if (imported == 0 && check["runner_only"]?.Value<bool?>() == true)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
return new JObject { ["success"] = true, ["imported"] = 0, ["runner_only"] = true, ["id"] = id, ["note"] = "Большой набор — используй HF id в QLoRA-раннере" };
|
Logs.Error($"AssistentImportHfDataset: {ex}");
|
||||||
|
return new JObject { ["error"] = ex.Message };
|
||||||
}
|
}
|
||||||
return new JObject
|
}
|
||||||
|
|
||||||
|
static string HfCellString(JToken tok)
|
||||||
|
{
|
||||||
|
if (tok is null || tok.Type == JTokenType.Null)
|
||||||
{
|
{
|
||||||
["success"] = true,
|
return "";
|
||||||
["imported"] = imported,
|
}
|
||||||
["id"] = id,
|
if (tok is JArray arr)
|
||||||
["rows_fetched"] = rows.Count,
|
{
|
||||||
["status"] = "draft",
|
List<string> parts = [];
|
||||||
};
|
foreach (JToken t in arr)
|
||||||
|
{
|
||||||
|
string s = t?.Type == JTokenType.String ? t.ToString() : t?.ToString(Newtonsoft.Json.Formatting.None);
|
||||||
|
if (!string.IsNullOrWhiteSpace(s))
|
||||||
|
{
|
||||||
|
parts.Add(s.Trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string.Join(", ", parts);
|
||||||
|
}
|
||||||
|
return tok.ToString().Trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
static JArray ConvertHfRowToMessages(JObject row, JObject schema, JObject mapping)
|
static JArray ConvertHfRowToMessages(JObject row, JObject schema, JObject mapping)
|
||||||
@@ -570,14 +619,14 @@ public partial class SwarmAssistentExtension
|
|||||||
}
|
}
|
||||||
if (kind == "fiction_tags_text")
|
if (kind == "fiction_tags_text")
|
||||||
{
|
{
|
||||||
string text = row["text"]?.ToString() ?? "";
|
string text = HfCellString(row["text"]);
|
||||||
if (string.IsNullOrWhiteSpace(text))
|
if (string.IsNullOrWhiteSpace(text))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
List<string> userParts = [];
|
List<string> userParts = [];
|
||||||
string title = row["title"]?.ToString()?.Trim();
|
string title = HfCellString(row["title"]);
|
||||||
string tags = row["tags"]?.ToString()?.Trim();
|
string tags = HfCellString(row["tags"]);
|
||||||
if (!string.IsNullOrWhiteSpace(title))
|
if (!string.IsNullOrWhiteSpace(title))
|
||||||
{
|
{
|
||||||
userParts.Add($"Title: {title}");
|
userParts.Add($"Title: {title}");
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ public sealed partial class AssistentMemory
|
|||||||
}
|
}
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
EnsureOpen();
|
EnsureTrainingReady();
|
||||||
if (!HasColumn("train_samples", "agent_linked"))
|
if (!HasColumn("train_samples", "agent_linked"))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -68,7 +68,14 @@ public sealed partial class AssistentMemory
|
|||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
EnsureOpen();
|
try
|
||||||
|
{
|
||||||
|
EnsureTrainingReady();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (!HasColumn("train_samples", "agent_linked"))
|
if (!HasColumn("train_samples", "agent_linked"))
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+75
-45
@@ -68,7 +68,7 @@ public sealed partial class AssistentMemory
|
|||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
EnsureOpen();
|
EnsureTrainingReady();
|
||||||
int take = Math.Clamp(limit, 1, 2000);
|
int take = Math.Clamp(limit, 1, 2000);
|
||||||
List<string> where = [];
|
List<string> where = [];
|
||||||
if (!string.IsNullOrWhiteSpace(status) && !string.Equals(status, "all", StringComparison.OrdinalIgnoreCase))
|
if (!string.IsNullOrWhiteSpace(status) && !string.Equals(status, "all", StringComparison.OrdinalIgnoreCase))
|
||||||
@@ -167,57 +167,87 @@ public sealed partial class AssistentMemory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int ImportTrainSamplesBatch(IEnumerable<JObject> samples)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
EnsureTrainingReady();
|
||||||
|
int imported = 0;
|
||||||
|
using SqliteTransaction tx = _conn.BeginTransaction();
|
||||||
|
foreach (JObject sample in samples)
|
||||||
|
{
|
||||||
|
if (sample is null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
UpsertTrainSampleCore(sample, tx);
|
||||||
|
imported++;
|
||||||
|
}
|
||||||
|
tx.Commit();
|
||||||
|
return imported;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public JObject UpsertTrainSample(JObject sample)
|
public JObject UpsertTrainSample(JObject sample)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
EnsureOpen();
|
EnsureTrainingReady();
|
||||||
long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
return UpsertTrainSampleCore(sample, null);
|
||||||
string id = sample["id"]?.ToString()?.Trim();
|
|
||||||
if (string.IsNullOrWhiteSpace(id))
|
|
||||||
{
|
|
||||||
id = $"ts_{now}_{Guid.NewGuid():N}"[..24];
|
|
||||||
}
|
|
||||||
string datasetId = sample["dataset_id"]?.ToString()?.Trim() ?? "default";
|
|
||||||
JArray messages = sample["messages"] as JArray ?? [];
|
|
||||||
using SqliteCommand cmd = _conn.CreateCommand();
|
|
||||||
cmd.CommandText =
|
|
||||||
"""
|
|
||||||
INSERT INTO train_samples(id, dataset_id, source, chat_id, persona, pack, hf_repo, messages_json, status, created_at, updated_at)
|
|
||||||
VALUES($id, $ds, $src, $chat, $persona, $pack, $hf, $msg, $status, $c, $u)
|
|
||||||
ON CONFLICT(id) DO UPDATE SET
|
|
||||||
dataset_id = excluded.dataset_id,
|
|
||||||
source = excluded.source,
|
|
||||||
chat_id = excluded.chat_id,
|
|
||||||
persona = excluded.persona,
|
|
||||||
pack = excluded.pack,
|
|
||||||
hf_repo = excluded.hf_repo,
|
|
||||||
messages_json = excluded.messages_json,
|
|
||||||
status = excluded.status,
|
|
||||||
updated_at = excluded.updated_at
|
|
||||||
""";
|
|
||||||
cmd.Parameters.AddWithValue("$id", id);
|
|
||||||
cmd.Parameters.AddWithValue("$ds", datasetId);
|
|
||||||
cmd.Parameters.AddWithValue("$src", sample["source"]?.ToString() ?? "manual");
|
|
||||||
cmd.Parameters.AddWithValue("$chat", (object)sample["chat_id"]?.ToString() ?? DBNull.Value);
|
|
||||||
cmd.Parameters.AddWithValue("$persona", (object)sample["persona"]?.ToString() ?? DBNull.Value);
|
|
||||||
cmd.Parameters.AddWithValue("$pack", (object)sample["pack"]?.ToString() ?? DBNull.Value);
|
|
||||||
cmd.Parameters.AddWithValue("$hf", (object)sample["hf_repo"]?.ToString() ?? DBNull.Value);
|
|
||||||
cmd.Parameters.AddWithValue("$msg", messages.ToString(Newtonsoft.Json.Formatting.None));
|
|
||||||
cmd.Parameters.AddWithValue("$status", sample["status"]?.ToString() ?? "draft");
|
|
||||||
long created = sample["createdAt"]?.Value<long?>() ?? now;
|
|
||||||
cmd.Parameters.AddWithValue("$c", created);
|
|
||||||
cmd.Parameters.AddWithValue("$u", now);
|
|
||||||
cmd.ExecuteNonQuery();
|
|
||||||
return new JObject { ["id"] = id, ["updatedAt"] = now };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JObject UpsertTrainSampleCore(JObject sample, SqliteTransaction tx)
|
||||||
|
{
|
||||||
|
long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||||
|
string id = sample["id"]?.ToString()?.Trim();
|
||||||
|
if (string.IsNullOrWhiteSpace(id))
|
||||||
|
{
|
||||||
|
id = $"ts_{now}_{Guid.NewGuid():N}"[..24];
|
||||||
|
}
|
||||||
|
string datasetId = sample["dataset_id"]?.ToString()?.Trim() ?? "default";
|
||||||
|
JArray messages = sample["messages"] as JArray ?? [];
|
||||||
|
using SqliteCommand cmd = _conn.CreateCommand();
|
||||||
|
if (tx is not null)
|
||||||
|
{
|
||||||
|
cmd.Transaction = tx;
|
||||||
|
}
|
||||||
|
cmd.CommandText =
|
||||||
|
"""
|
||||||
|
INSERT INTO train_samples(id, dataset_id, source, chat_id, persona, pack, hf_repo, messages_json, status, created_at, updated_at)
|
||||||
|
VALUES($id, $ds, $src, $chat, $persona, $pack, $hf, $msg, $status, $c, $u)
|
||||||
|
ON CONFLICT(id) DO UPDATE SET
|
||||||
|
dataset_id = excluded.dataset_id,
|
||||||
|
source = excluded.source,
|
||||||
|
chat_id = excluded.chat_id,
|
||||||
|
persona = excluded.persona,
|
||||||
|
pack = excluded.pack,
|
||||||
|
hf_repo = excluded.hf_repo,
|
||||||
|
messages_json = excluded.messages_json,
|
||||||
|
status = excluded.status,
|
||||||
|
updated_at = excluded.updated_at
|
||||||
|
""";
|
||||||
|
cmd.Parameters.AddWithValue("$id", id);
|
||||||
|
cmd.Parameters.AddWithValue("$ds", datasetId);
|
||||||
|
cmd.Parameters.AddWithValue("$src", sample["source"]?.ToString() ?? "manual");
|
||||||
|
cmd.Parameters.AddWithValue("$chat", (object)sample["chat_id"]?.ToString() ?? DBNull.Value);
|
||||||
|
cmd.Parameters.AddWithValue("$persona", (object)sample["persona"]?.ToString() ?? DBNull.Value);
|
||||||
|
cmd.Parameters.AddWithValue("$pack", (object)sample["pack"]?.ToString() ?? DBNull.Value);
|
||||||
|
cmd.Parameters.AddWithValue("$hf", (object)sample["hf_repo"]?.ToString() ?? DBNull.Value);
|
||||||
|
cmd.Parameters.AddWithValue("$msg", messages.ToString(Newtonsoft.Json.Formatting.None));
|
||||||
|
cmd.Parameters.AddWithValue("$status", sample["status"]?.ToString() ?? "draft");
|
||||||
|
long created = sample["createdAt"]?.Value<long?>() ?? now;
|
||||||
|
cmd.Parameters.AddWithValue("$c", created);
|
||||||
|
cmd.Parameters.AddWithValue("$u", now);
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
return new JObject { ["id"] = id, ["updatedAt"] = now };
|
||||||
|
}
|
||||||
|
|
||||||
public bool DeleteTrainSample(string id)
|
public bool DeleteTrainSample(string id)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
EnsureOpen();
|
EnsureTrainingReady();
|
||||||
using SqliteCommand cmd = _conn.CreateCommand();
|
using SqliteCommand cmd = _conn.CreateCommand();
|
||||||
cmd.CommandText = "DELETE FROM train_samples WHERE id = $id";
|
cmd.CommandText = "DELETE FROM train_samples WHERE id = $id";
|
||||||
cmd.Parameters.AddWithValue("$id", id ?? "");
|
cmd.Parameters.AddWithValue("$id", id ?? "");
|
||||||
@@ -229,7 +259,7 @@ public sealed partial class AssistentMemory
|
|||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
EnsureOpen();
|
EnsureTrainingReady();
|
||||||
using SqliteCommand cmd = _conn.CreateCommand();
|
using SqliteCommand cmd = _conn.CreateCommand();
|
||||||
if (string.IsNullOrWhiteSpace(status) || string.Equals(status, "all", StringComparison.OrdinalIgnoreCase))
|
if (string.IsNullOrWhiteSpace(status) || string.Equals(status, "all", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
@@ -288,7 +318,7 @@ public sealed partial class AssistentMemory
|
|||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
EnsureOpen();
|
EnsureTrainingReady();
|
||||||
using SqliteCommand cmd = _conn.CreateCommand();
|
using SqliteCommand cmd = _conn.CreateCommand();
|
||||||
cmd.CommandText = "SELECT id, kind, status, config_json, base_model, output_name, log_path, progress_json, created_at, updated_at, finished_at FROM train_jobs WHERE id = $id";
|
cmd.CommandText = "SELECT id, kind, status, config_json, base_model, output_name, log_path, progress_json, created_at, updated_at, finished_at FROM train_jobs WHERE id = $id";
|
||||||
cmd.Parameters.AddWithValue("$id", id ?? "");
|
cmd.Parameters.AddWithValue("$id", id ?? "");
|
||||||
@@ -305,7 +335,7 @@ public sealed partial class AssistentMemory
|
|||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
EnsureOpen();
|
EnsureTrainingReady();
|
||||||
using SqliteCommand cmd = _conn.CreateCommand();
|
using SqliteCommand cmd = _conn.CreateCommand();
|
||||||
cmd.CommandText =
|
cmd.CommandText =
|
||||||
"SELECT id, kind, status, config_json, base_model, output_name, log_path, progress_json, created_at, updated_at, finished_at "
|
"SELECT id, kind, status, config_json, base_model, output_name, log_path, progress_json, created_at, updated_at, finished_at "
|
||||||
@@ -341,7 +371,7 @@ public sealed partial class AssistentMemory
|
|||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
EnsureOpen();
|
EnsureTrainingReady();
|
||||||
using SqliteCommand cmd = _conn.CreateCommand();
|
using SqliteCommand cmd = _conn.CreateCommand();
|
||||||
cmd.CommandText = "SELECT id, kind, status, config_json, base_model, output_name, log_path, progress_json, created_at, updated_at, finished_at FROM train_jobs WHERE status IN ('pending','running') ORDER BY updated_at DESC LIMIT 1";
|
cmd.CommandText = "SELECT id, kind, status, config_json, base_model, output_name, log_path, progress_json, created_at, updated_at, finished_at FROM train_jobs WHERE status IN ('pending','running') ORDER BY updated_at DESC LIMIT 1";
|
||||||
using SqliteDataReader r = cmd.ExecuteReader();
|
using SqliteDataReader r = cmd.ExecuteReader();
|
||||||
|
|||||||
+28
-1
@@ -149,7 +149,7 @@ public sealed partial class AssistentMemory : IDisposable
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logs.Debug($"AssistentMemory training schema: {ex.Message}");
|
Logs.Error($"AssistentMemory training schema failed: {ex.Message}");
|
||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -166,6 +166,10 @@ public sealed partial class AssistentMemory : IDisposable
|
|||||||
|
|
||||||
bool HasColumn(string table, string column)
|
bool HasColumn(string table, string column)
|
||||||
{
|
{
|
||||||
|
if (!HasTable(table))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
using SqliteCommand cmd = _conn.CreateCommand();
|
using SqliteCommand cmd = _conn.CreateCommand();
|
||||||
cmd.CommandText = $"PRAGMA table_info({table})";
|
cmd.CommandText = $"PRAGMA table_info({table})";
|
||||||
using SqliteDataReader reader = cmd.ExecuteReader();
|
using SqliteDataReader reader = cmd.ExecuteReader();
|
||||||
@@ -179,6 +183,29 @@ public sealed partial class AssistentMemory : IDisposable
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HasTable(string table)
|
||||||
|
{
|
||||||
|
using SqliteCommand cmd = _conn.CreateCommand();
|
||||||
|
cmd.CommandText = "SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = $n LIMIT 1";
|
||||||
|
cmd.Parameters.AddWithValue("$n", table ?? "");
|
||||||
|
return cmd.ExecuteScalar() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void EnsureTrainingReady()
|
||||||
|
{
|
||||||
|
EnsureOpen();
|
||||||
|
if (!HasTable("train_samples"))
|
||||||
|
{
|
||||||
|
EnsureTrainingSchema();
|
||||||
|
}
|
||||||
|
if (!HasTable("train_samples"))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"Assistent training database unavailable (train_samples). "
|
||||||
|
+ "Run gpu-rent seed-extensions and restart SwarmUI (≥0.15.6).");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MigratePersonaColumn()
|
void MigratePersonaColumn()
|
||||||
{
|
{
|
||||||
if (HasColumn("memories", "persona"))
|
if (HasColumn("memories", "persona"))
|
||||||
|
|||||||
@@ -25,14 +25,21 @@ public partial class SwarmAssistentExtension
|
|||||||
public async Task<JObject> AssistentGetDatasetAgentSettings(Session session)
|
public async Task<JObject> AssistentGetDatasetAgentSettings(Session session)
|
||||||
{
|
{
|
||||||
await Task.CompletedTask;
|
await Task.CompletedTask;
|
||||||
JObject settings = Config.LoadTrainingAgent();
|
try
|
||||||
return new JObject
|
|
||||||
{
|
{
|
||||||
["success"] = true,
|
JObject settings = Config.LoadTrainingAgent();
|
||||||
["settings"] = settings,
|
return new JObject
|
||||||
["linked"] = Memory.CountAgentLinkedTrainSamples(),
|
{
|
||||||
["approved"] = Memory.CountTrainSamples("approved"),
|
["success"] = true,
|
||||||
};
|
["settings"] = settings,
|
||||||
|
["linked"] = Memory.CountAgentLinkedTrainSamples(),
|
||||||
|
["approved"] = Memory.CountTrainSamples("approved"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new JObject { ["error"] = ex.Message };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<JObject> AssistentSaveDatasetAgentSettings(Session session, JObject settings)
|
public async Task<JObject> AssistentSaveDatasetAgentSettings(Session session, JObject settings)
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public partial class SwarmAssistentExtension : Extension
|
|||||||
ExtensionAuthor = "mrleo1nid";
|
ExtensionAuthor = "mrleo1nid";
|
||||||
Description = "Collaborative Krea 2 assistant: Ollama chat, persona presets, vector memory, model cards, Generate loop.";
|
Description = "Collaborative Krea 2 assistant: Ollama chat, persona presets, vector memory, model cards, Generate loop.";
|
||||||
License = "MIT";
|
License = "MIT";
|
||||||
Version = "0.15.11";
|
Version = "0.15.12";
|
||||||
Tags = ["tabs", "ui", "llm", "ollama", "krea", "inpaint", "memory", "training", "heard", "qlora"];
|
Tags = ["tabs", "ui", "llm", "ollama", "krea", "inpaint", "memory", "training", "heard", "qlora"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
[
|
||||||
|
"catalog_card",
|
||||||
|
"fromCards",
|
||||||
|
"AssistentGetCard",
|
||||||
|
"AssistentSaveCard",
|
||||||
|
"AssistentSearchCivitai",
|
||||||
|
"AssistentListWanted",
|
||||||
|
"AssistentEnqueueWanted",
|
||||||
|
"buildCivitaiCards",
|
||||||
|
"maybeWriteCardAfterDownload",
|
||||||
|
"sa_tab_cards",
|
||||||
|
"modelCards",
|
||||||
|
"extractCardJson",
|
||||||
|
"userImpliesGenerate",
|
||||||
|
"fromEmptyPatchRetry",
|
||||||
|
"synthesizePatchAfterEmptyFence"
|
||||||
|
]
|
||||||
+33
-15
@@ -50,7 +50,7 @@ export function attachTraining(SA) {
|
|||||||
setAgentHeardStats(state.agentLinked);
|
setAgentHeardStats(state.agentLinked);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const msg = String(e.message || e);
|
const msg = String(e.message || e);
|
||||||
setTrainStatus(isSqliteError(msg) ? `${msg} — ${sqliteHint()}` : msg);
|
setTrainStatus(formatTrainError(msg));
|
||||||
console.warn('loadAgentHeardSettings', e);
|
console.warn('loadAgentHeardSettings', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,7 +66,7 @@ export function attachTraining(SA) {
|
|||||||
state.agentSettings = data?.settings || settings;
|
state.agentSettings = data?.settings || settings;
|
||||||
setTrainStatus('Настройки «услышанного» сохранены');
|
setTrainStatus('Настройки «услышанного» сохранены');
|
||||||
} catch (e) {
|
} 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 ?? '—'}`);
|
setTrainStatus(`Подключено: +${data?.linked_now ?? 0}, всего ${data?.total_linked ?? '—'}`);
|
||||||
await refreshSamples();
|
await refreshSamples();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setTrainStatus(String(e.message || e));
|
setTrainStatus(formatTrainError(e.message || e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +91,25 @@ export function attachTraining(SA) {
|
|||||||
|
|
||||||
function isSqliteError(msg) {
|
function isSqliteError(msg) {
|
||||||
const s = String(msg || '').toLowerCase();
|
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() {
|
function sqliteHint() {
|
||||||
@@ -167,7 +185,7 @@ export function attachTraining(SA) {
|
|||||||
const data = await SA.request('AssistentListTrainSamples', { status, persona, limit: 300 });
|
const data = await SA.request('AssistentListTrainSamples', { status, persona, limit: 300 });
|
||||||
if (data?.error) {
|
if (data?.error) {
|
||||||
const msg = data.error;
|
const msg = data.error;
|
||||||
setTrainStatus(isSqliteError(msg) ? `${msg} — ${sqliteHint()}` : msg);
|
setTrainStatus(formatTrainError(msg));
|
||||||
const stats = $('sa_train_stats');
|
const stats = $('sa_train_stats');
|
||||||
if (stats) stats.textContent = 'Датасет недоступен (SQLite)';
|
if (stats) stats.textContent = 'Датасет недоступен (SQLite)';
|
||||||
state.samples = [];
|
state.samples = [];
|
||||||
@@ -196,7 +214,7 @@ export function attachTraining(SA) {
|
|||||||
}
|
}
|
||||||
renderSamples();
|
renderSamples();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setTrainStatus(String(e.message || e));
|
setTrainStatus(formatTrainError(e.message || e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -441,7 +459,7 @@ export function attachTraining(SA) {
|
|||||||
renderHfList();
|
renderHfList();
|
||||||
setTrainStatus(`Найдено: ${state.hfResults.length}`);
|
setTrainStatus(`Найдено: ${state.hfResults.length}`);
|
||||||
} catch (e) {
|
} 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' });
|
$('sa_train_samples')?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const err = String(e.message || e);
|
const err = String(e.message || e);
|
||||||
const show = isSqliteError(err) ? `${err} — ${sqliteHint()}` : err;
|
const show = formatTrainError(err);
|
||||||
setTrainStatus(show);
|
setTrainStatus(show);
|
||||||
if (hfSt) hfSt.textContent = show;
|
if (hfSt) hfSt.textContent = show;
|
||||||
} finally {
|
} finally {
|
||||||
@@ -578,7 +596,7 @@ export function attachTraining(SA) {
|
|||||||
setTrainStatus(`Готово: ${data.name}`);
|
setTrainStatus(`Готово: ${data.name}`);
|
||||||
SA.app?.refreshModels?.();
|
SA.app?.refreshModels?.();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setTrainStatus(String(e.message || e));
|
setTrainStatus(formatTrainError(e.message || e));
|
||||||
} finally {
|
} finally {
|
||||||
btn?.removeAttribute('disabled');
|
btn?.removeAttribute('disabled');
|
||||||
}
|
}
|
||||||
@@ -712,7 +730,7 @@ export function attachTraining(SA) {
|
|||||||
setTrainingTab('train');
|
setTrainingTab('train');
|
||||||
setTrainStatus('QLoRA запущена — прогресс ниже');
|
setTrainStatus('QLoRA запущена — прогресс ниже');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setTrainStatus(String(e.message || e));
|
setTrainStatus(formatTrainError(e.message || e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -722,7 +740,7 @@ export function attachTraining(SA) {
|
|||||||
setTrainingLock(false);
|
setTrainingLock(false);
|
||||||
setTrainStatus('Отменено');
|
setTrainStatus('Отменено');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setTrainStatus(String(e.message || e));
|
setTrainStatus(formatTrainError(e.message || e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -760,7 +778,7 @@ export function attachTraining(SA) {
|
|||||||
});
|
});
|
||||||
setTrainStatus('Раннер сохранён');
|
setTrainStatus('Раннер сохранён');
|
||||||
} catch (e) {
|
} 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 });
|
const data = await SA.request('AssistentImportDataset', { format: 'auto', content: text });
|
||||||
setTrainStatus(`Импорт: ${data.imported}`);
|
setTrainStatus(`Импорт: ${data.imported}`);
|
||||||
await refreshSamples();
|
await refreshSamples();
|
||||||
} catch (err) { setTrainStatus(String(err.message || err)); }
|
} catch (err) { setTrainStatus(formatTrainError(err.message || err)); }
|
||||||
e.target.value = '';
|
e.target.value = '';
|
||||||
});
|
});
|
||||||
$('sa_btn_train_export')?.addEventListener('click', async () => {
|
$('sa_btn_train_export')?.addEventListener('click', async () => {
|
||||||
@@ -842,7 +860,7 @@ export function attachTraining(SA) {
|
|||||||
setAgentHeardStats(state.agentLinked);
|
setAgentHeardStats(state.agentLinked);
|
||||||
setTrainStatus('Пример подключён к агенту');
|
setTrainStatus('Пример подключён к агенту');
|
||||||
await refreshSamples();
|
await refreshSamples();
|
||||||
} catch (err) { setTrainStatus(String(err.message || err)); }
|
} catch (err) { setTrainStatus(formatTrainError(err.message || err)); }
|
||||||
} else if (e.target.closest('[data-unlink]')) {
|
} else if (e.target.closest('[data-unlink]')) {
|
||||||
try {
|
try {
|
||||||
const data = await SA.request('AssistentUnlinkTrainSampleFromAgent', { id });
|
const data = await SA.request('AssistentUnlinkTrainSampleFromAgent', { id });
|
||||||
@@ -850,7 +868,7 @@ export function attachTraining(SA) {
|
|||||||
setAgentHeardStats(state.agentLinked);
|
setAgentHeardStats(state.agentLinked);
|
||||||
setTrainStatus('Пример отключён от агента');
|
setTrainStatus('Пример отключён от агента');
|
||||||
await refreshSamples();
|
await refreshSamples();
|
||||||
} catch (err) { setTrainStatus(String(err.message || err)); }
|
} catch (err) { setTrainStatus(formatTrainError(err.message || err)); }
|
||||||
} else if (e.target.closest('[data-del]')) {
|
} else if (e.target.closest('[data-del]')) {
|
||||||
if (window.confirm('Удалить пример?')) {
|
if (window.confirm('Удалить пример?')) {
|
||||||
await SA.request('AssistentDeleteTrainSample', { id });
|
await SA.request('AssistentDeleteTrainSample', { id });
|
||||||
|
|||||||
Reference in New Issue
Block a user