diff --git a/javascript/_utils.js b/javascript/_utils.js index 45ff5e8..fc823a6 100644 --- a/javascript/_utils.js +++ b/javascript/_utils.js @@ -173,7 +173,16 @@ function flatten(obj, roots = [], sep = ".") { } // Calculate biased tag score based on post count and frequent usage -function calculateUsageBias(count, uses) { +function calculateUsageBias(result, count, uses, lastUseDate) { + // Guard for minimum usage count & last usage date + const diffTime = Math.abs(Date.now() - (lastUseDate || Date.now())); + const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); + if (uses < TAC_CFG.frequencyMinCount || diffDays > TAC_CFG.frequencyMaxAge) { + uses = 0; + } else if (uses != 0) { + result.usageBias = true; + } + switch (TAC_CFG.frequencyFunction) { case "Logarithmic (weak)": return Math.log(1 + count) + Math.log(1 + uses); @@ -187,7 +196,14 @@ function calculateUsageBias(count, uses) { } // Beautify return type for easier parsing function mapUseCountArray(useCounts) { - return useCounts.map(useCount => {return {"name": useCount[0], "type": useCount[1], "count": useCount[2]}}); + return useCounts.map(useCount => { + return { + "name": useCount[0], + "type": useCount[1], + "count": useCount[2], + "lastUseDate": useCount[3] + } + }); } // Call API endpoint to increase bias of tag in the database function increaseUseCount(tagName, type, negative = false) { diff --git a/javascript/tagAutocomplete.js b/javascript/tagAutocomplete.js index b28f3f0..5b29ddb 100644 --- a/javascript/tagAutocomplete.js +++ b/javascript/tagAutocomplete.js @@ -1,4 +1,4 @@ -const styleColors = { +const styleColors = { "--results-bg": ["#0b0f19", "#ffffff"], "--results-border-color": ["#4b5563", "#e5e7eb"], "--results-border-width": ["1px", "1.5px"], @@ -224,6 +224,8 @@ async function syncOptions() { modelSortOrder: opts["tac_modelSortOrder"], frequencySort: opts["tac_frequencySort"], frequencyFunction: opts["tac_frequencyFunction"], + frequencyMinCount: opts["tac_frequencyMinCount"], + frequencyMaxAge: opts["tac_frequencyMaxAge"], // Insertion related settings replaceUnderscores: opts["tac_replaceUnderscores"], escapeParentheses: opts["tac_escapeParentheses"], @@ -1169,7 +1171,6 @@ async function autocomplete(textArea, prompt, fixedTag = null) { // Request use counts from the DB const counts = await getUseCounts(names, types, isNegative); - const usedResults = counts.filter(c => c.count > 0).map(c => c.name); // Sort all results = results.sort((a, b) => { @@ -1178,19 +1179,16 @@ async function autocomplete(textArea, prompt, fixedTag = null) { const aUseStats = counts.find(c => c.name === aName && c.type === a.type); const bUseStats = counts.find(c => c.name === bName && c.type === b.type); + const aUses = aUseStats?.count || 0; + const bUses = bUseStats?.count || 0; + const aLastUseDate = Date.parse(aUseStats?.lastUseDate); + const bLastUseDate = Date.parse(bUseStats?.lastUseDate); - const aWeight = calculateUsageBias(a.count, aUseStats ? aUseStats.count : 0); - const bWeight = calculateUsageBias(b.count, bUseStats ? bUseStats.count : 0); + const aWeight = calculateUsageBias(a, a.count, aUses, aLastUseDate); + const bWeight = calculateUsageBias(b, b.count, bUses, bLastUseDate); return bWeight - aWeight; }); - - // Mark results - results.forEach(r => { - const name = r.type === ResultType.chant ? r.aliases : r.text; - if (usedResults.includes(name)) - r.usageBias = true; - }); } // Slice if the user has set a max result count and we are not in a extra networks / wildcard list diff --git a/scripts/tag_autocomplete_helper.py b/scripts/tag_autocomplete_helper.py index 43f6c39..1ef64e8 100644 --- a/scripts/tag_autocomplete_helper.py +++ b/scripts/tag_autocomplete_helper.py @@ -421,6 +421,8 @@ def on_ui_settings(): "tac_modelSortOrder": shared.OptionInfo("Name", "Model sort order", gr.Dropdown, lambda: {"choices": list(sort_criteria.keys())}).info("Order for extra network models and wildcards in dropdown"), "tac_frequencySort": shared.OptionInfo(True, "Locally record tag usage and sort frequent tags higher").info("Will also work for extra networks, keeping the specified base order"), "tac_frequencyFunction": shared.OptionInfo("Logarithmic (weak)", "Function to use for frequency sorting", gr.Dropdown, lambda: {"choices": list(frequency_sort_functions.keys())}).info("; ".join([f'{key}: {val}' for key, val in frequency_sort_functions.items()])), + "tac_frequencyMinCount": shared.OptionInfo(3, "Minimum number of uses for a tag to be considered frequent").info("Tags with less uses than this will not be sorted higher, even if the sorting function would normally result in a higher position."), + "tac_frequencyMaxAge": shared.OptionInfo(30, "Maximum days since last use for a tag to be considered frequent").info("Similar to the above, tags that haven't been used in this many days will not be sorted higher."), # Insertion related settings "tac_replaceUnderscores": shared.OptionInfo(True, "Replace underscores with spaces on insertion"), "tac_escapeParentheses": shared.OptionInfo(True, "Escape parentheses on insertion"), @@ -597,7 +599,7 @@ def api_tac(_: gr.Blocks, app: FastAPI): if get: ret = func() if ret is list: - ret = [{"name": t[0], "type": t[1], "count": t[2]} for t in ret] + ret = [{"name": t[0], "type": t[1], "count": t[2], "lastUseDate": t[3]} for t in ret] return JSONResponse({"result": ret}) else: func() diff --git a/scripts/tag_frequency_db.py b/scripts/tag_frequency_db.py index 4f4f26b..c0fae9d 100644 --- a/scripts/tag_frequency_db.py +++ b/scripts/tag_frequency_db.py @@ -91,7 +91,7 @@ class TagFrequencyDb: with transaction() as cursor: cursor.execute( f""" - SELECT name, type, {count_str} + SELECT name, type, {count_str}, last_used FROM tag_frequency ORDER BY {count_str} DESC """ @@ -105,15 +105,15 @@ class TagFrequencyDb: with transaction() as cursor: cursor.execute( f""" - SELECT {count_str} + SELECT {count_str}, last_used FROM tag_frequency WHERE name = ? AND type = ? """, - (tag,ttype), + (tag, ttype), ) tag_count = cursor.fetchone() - return tag_count[0] if tag_count else 0 + return tag_count[0], tag_count[1] if tag_count else 0 def get_tag_counts(self, tags: list[str], ttypes: list[str], negative=False): count_str = "count_neg" if negative else "count_pos" @@ -121,18 +121,18 @@ class TagFrequencyDb: for tag, ttype in zip(tags, ttypes): cursor.execute( f""" - SELECT {count_str} + SELECT {count_str}, last_used FROM tag_frequency WHERE name = ? AND type = ? """, - (tag,ttype), + (tag, ttype), ) tag_count = cursor.fetchone() - yield (tag, ttype, tag_count[0]) if tag_count else (tag, ttype, 0) + yield (tag, ttype, tag_count[0], tag_count[1]) if tag_count else (tag, ttype, 0) def increase_tag_count(self, tag, ttype, negative=False): - pos_count = self.get_tag_count(tag, ttype, False) - neg_count = self.get_tag_count(tag, ttype, True) + pos_count = self.get_tag_count(tag, ttype, False)[0] + neg_count = self.get_tag_count(tag, ttype, True)[0] if negative: neg_count += 1 @@ -156,7 +156,7 @@ class TagFrequencyDb: set_str = "count_pos = 0" elif negative: set_str = "count_neg = 0" - + with transaction() as cursor: cursor.execute( f""" @@ -164,5 +164,5 @@ class TagFrequencyDb: SET {set_str} WHERE name = ? AND type = ? """, - (tag,ttype), + (tag, ttype), )