Tags from the tagger are added after a comma and the caption is never reworded. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MpnF7GwNGMbngU11wjym7P
56 lines
2.7 KiB
JavaScript
56 lines
2.7 KiB
JavaScript
// Captions as tags, the same rules as the server's modules/training/lora/captions.py.
|
|
|
|
export const splitTags = (text) => {
|
|
const seen = new Set(), out = [];
|
|
for (const part of String(text ?? "").replace(/\n/g, ",").split(",")) {
|
|
const tag = part.split(/\s+/).filter(Boolean).join(" ");
|
|
if (tag && !seen.has(tag.toLowerCase())) { seen.add(tag.toLowerCase()); out.push(tag); }
|
|
}
|
|
return out;
|
|
};
|
|
export const joinTags = (tags) => splitTags((tags || []).join(", ")).join(", ").slice(0, 4000);
|
|
|
|
const same = (a, b) => String(a).split(/\s+/).filter(Boolean).join(" ").toLowerCase() === String(b).split(/\s+/).filter(Boolean).join(" ").toLowerCase();
|
|
/** The comma separated pieces of a caption as typed: trimmed, no empties, nothing dropped or reworded. */
|
|
export const parts = (text) => String(text ?? "").split(",").map((p) => p.trim()).filter(Boolean);
|
|
|
|
/** The caption with these tags added at its end after a comma, skipping any it has; the caption itself stays as typed. */
|
|
export function addTags(caption, tags) {
|
|
const have = parts(caption);
|
|
const fresh = splitTags((tags || []).join(", ")).filter((t) => !have.some((h) => same(t, h)));
|
|
const text = String(caption ?? "").trimEnd().replace(/,+$/, "").trimEnd();
|
|
return (text ? [text, ...fresh] : fresh).join(", ").slice(0, 4000);
|
|
}
|
|
|
|
/** Every tag with how many captions hold it, most common first. */
|
|
export function frequency(items) {
|
|
const counts = new Map();
|
|
for (const item of items) for (const tag of splitTags(item.caption)) {
|
|
const key = tag.toLowerCase(), have = counts.get(key);
|
|
if (have) have.count += 1; else counts.set(key, { tag, count: 1 });
|
|
}
|
|
return [...counts.values()].sort((a, b) => b.count - a.count || a.tag.localeCompare(b.tag));
|
|
}
|
|
|
|
/** Change captions in place; returns how many changed. op: add, prepend, remove, replace. `ids` (a Set) limits it. */
|
|
export function bulk(items, op, tag, to, ids) {
|
|
tag = String(tag ?? "").split(/\s+/).filter(Boolean).join(" ");
|
|
if (!tag || !["add", "prepend", "remove", "replace"].includes(op)) return 0;
|
|
to = String(to ?? "").split(/\s+/).filter(Boolean).join(" ");
|
|
if (op === "replace" && !to) op = "remove";
|
|
let changed = 0;
|
|
for (const item of items) {
|
|
if (ids && !ids.has(item.id)) continue;
|
|
let tags = parts(item.caption);
|
|
const has = tags.some((t) => same(t, tag));
|
|
if (op === "add" && !has) tags.push(tag);
|
|
else if (op === "prepend") tags = [tag, ...tags.filter((t) => !same(t, tag))];
|
|
else if (op === "remove" && has) tags = tags.filter((t) => !same(t, tag));
|
|
else if (op === "replace" && has) tags = tags.map((t) => (same(t, tag) ? to : t));
|
|
else continue;
|
|
const text = tags.join(", ").slice(0, 4000);
|
|
if (text !== item.caption) { item.caption = text; changed += 1; }
|
|
}
|
|
return changed;
|
|
}
|