mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Working state stat UI
This commit is contained in:
@@ -2566,6 +2566,9 @@
|
|||||||
<input type="file" id="avatar_upload_file" accept="image/*" name="avatar">
|
<input type="file" id="avatar_upload_file" accept="image/*" name="avatar">
|
||||||
<input type="hidden" id="avatar_upload_overwrite" name="overwrite_name" value="">
|
<input type="hidden" id="avatar_upload_overwrite" name="overwrite_name" value="">
|
||||||
</form>
|
</form>
|
||||||
|
<button class="menu_button user_stats_button" title="Click for stats!" style="position: absolute; right: 0; bottom: 0;">
|
||||||
|
<i class="fa-solid fa-circle-info"></i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -2604,7 +2607,6 @@
|
|||||||
<div id="rm_button_selected_ch">
|
<div id="rm_button_selected_ch">
|
||||||
<h2></h2>
|
<h2></h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- end group peeking cope structure-->
|
<!-- end group peeking cope structure-->
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { humanizedDateTime, favsToHotswap, getMessageTimeStamp, dragElement, isMobile, AA_CountCharTime } from "./scripts/RossAscends-mods.js";
|
import { humanizedDateTime, humanizeGenTime, favsToHotswap, getMessageTimeStamp, dragElement, isMobile, AA_CountCharTime } from "./scripts/RossAscends-mods.js";
|
||||||
|
import { userStatsHandler, characterStatsHandler } from './scripts/stats.js';
|
||||||
import { encode } from "../scripts/gpt-2-3-tokenizer/mod.js";
|
import { encode } from "../scripts/gpt-2-3-tokenizer/mod.js";
|
||||||
import { GPT3BrowserTokenizer } from "../scripts/gpt-3-tokenizer/gpt3-tokenizer.js";
|
import { GPT3BrowserTokenizer } from "../scripts/gpt-3-tokenizer/gpt3-tokenizer.js";
|
||||||
import {
|
import {
|
||||||
@@ -955,7 +956,7 @@ async function getCharacters() {
|
|||||||
|
|
||||||
|
|
||||||
updateCharacterCount('#rm_print_characters_block > div');
|
updateCharacterCount('#rm_print_characters_block > div');
|
||||||
await AA_CountCharTime(this_chid);
|
//await AA_CountCharTime(this_chid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4895,7 +4896,6 @@ async function getSettings(type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
getStats();
|
|
||||||
if (data.result != "file not find" && data.settings) {
|
if (data.result != "file not find" && data.settings) {
|
||||||
settings = JSON.parse(data.settings);
|
settings = JSON.parse(data.settings);
|
||||||
if (settings.username !== undefined) {
|
if (settings.username !== undefined) {
|
||||||
@@ -5122,25 +5122,6 @@ async function saveSettings(type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async function getStats() {
|
|
||||||
const response = await fetch("/getstats", {
|
|
||||||
method: "POST",
|
|
||||||
headers: getRequestHeaders(),
|
|
||||||
body: JSON.stringify({}),
|
|
||||||
cache: "no-cache",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
toastr.error('Stats could not be loaded. Try reloading the page.');
|
|
||||||
throw new Error('Error getting stats');
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
charStats = data;
|
|
||||||
console.log(charStats);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function setCharacterBlockHeight() {
|
function setCharacterBlockHeight() {
|
||||||
const $children = $("#rm_print_characters_block").children();
|
const $children = $("#rm_print_characters_block").children();
|
||||||
@@ -8581,6 +8562,10 @@ $(document).ready(function () {
|
|||||||
restoreCaretPosition($(this).get(0), caretPosition);
|
restoreCaretPosition($(this).get(0), caretPosition);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(".user_stats_button").on('click', function () {
|
||||||
|
userStatsHandler(charStats);
|
||||||
|
});
|
||||||
|
|
||||||
$('#external_import_button').on('click', async () => {
|
$('#external_import_button').on('click', async () => {
|
||||||
const html = `<h3>Enter the URL of the content to import</h3>
|
const html = `<h3>Enter the URL of the content to import</h3>
|
||||||
Supported sources:<br>
|
Supported sources:<br>
|
||||||
@@ -8719,4 +8704,4 @@ $(document).ready(function () {
|
|||||||
$("#charListGridToggle").on('click', async () => {
|
$("#charListGridToggle").on('click', async () => {
|
||||||
doCharListDisplaySwitch();
|
doCharListDisplaySwitch();
|
||||||
});
|
});
|
||||||
})
|
});
|
@@ -16,6 +16,10 @@ import {
|
|||||||
charStats,
|
charStats,
|
||||||
} from "../script.js";
|
} from "../script.js";
|
||||||
|
|
||||||
|
import {
|
||||||
|
characterStatsHandler,
|
||||||
|
} from "./stats.js";
|
||||||
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
power_user,
|
power_user,
|
||||||
@@ -72,7 +76,7 @@ const observer = new MutationObserver(function (mutations) {
|
|||||||
RA_checkOnlineStatus();
|
RA_checkOnlineStatus();
|
||||||
} else if (mutation.target.parentNode === SelectedCharacterTab) {
|
} else if (mutation.target.parentNode === SelectedCharacterTab) {
|
||||||
setTimeout(RA_CountCharTokens, 200);
|
setTimeout(RA_CountCharTokens, 200);
|
||||||
setTimeout(AA_CountCharTime, 200);
|
//setTimeout(AA_CountCharTime, 200);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -108,10 +112,10 @@ function waitForElement(querySelector, timeout) {
|
|||||||
|
|
||||||
|
|
||||||
//humanize character generation time
|
//humanize character generation time
|
||||||
export function humanizeGenTime(character) {
|
export function humanizeGenTime(total_gen_time) {
|
||||||
let stat = charStats[character.avatar]
|
|
||||||
//convert time_spent to humanized format of "_ Hours, _ Minutes, _ Seconds" from milliseconds
|
//convert time_spent to humanized format of "_ Hours, _ Minutes, _ Seconds" from milliseconds
|
||||||
let time_spent = stat.total_gen_time
|
let time_spent = total_gen_time || 0;
|
||||||
time_spent = Math.floor(time_spent / 1000);
|
time_spent = Math.floor(time_spent / 1000);
|
||||||
let seconds = time_spent % 60;
|
let seconds = time_spent % 60;
|
||||||
time_spent = Math.floor(time_spent / 60);
|
time_spent = Math.floor(time_spent / 60);
|
||||||
@@ -327,7 +331,10 @@ export function RA_CountCharTokens() {
|
|||||||
// if neither, probably safety char or some error in loading
|
// if neither, probably safety char or some error in loading
|
||||||
} else { console.debug("RA_TC -- no valid char found, closing."); }
|
} else { console.debug("RA_TC -- no valid char found, closing."); }
|
||||||
}
|
}
|
||||||
$("#result_info").html(`<small>${count_tokens} Tokens (${perm_tokens} Permanent)</small>`);
|
//label rm_stats_button with a tooltip indicating stats
|
||||||
|
$("#result_info").html(`<small>${count_tokens} Tokens (${perm_tokens} Permanent)</small>
|
||||||
|
|
||||||
|
<i title='Click for stats!' class="fa-solid fa-circle-info rm_stats_button"></i>`);
|
||||||
// display the counted tokens
|
// display the counted tokens
|
||||||
const tokenLimit = Math.max(((main_api !== 'openai' ? max_context : oai_settings.openai_max_context) / 2), 1024);
|
const tokenLimit = Math.max(((main_api !== 'openai' ? max_context : oai_settings.openai_max_context) / 2), 1024);
|
||||||
if (count_tokens < tokenLimit && perm_tokens < tokenLimit) {
|
if (count_tokens < tokenLimit && perm_tokens < tokenLimit) {
|
||||||
@@ -339,10 +346,16 @@ export function RA_CountCharTokens() {
|
|||||||
<small class="flex-container flexnowrap flexNoGap">
|
<small class="flex-container flexnowrap flexNoGap">
|
||||||
<div class="neutral_warning">${count_tokens}</div> Tokens (<div class="neutral_warning">${perm_tokens}</div><div> Permanent)</div>
|
<div class="neutral_warning">${count_tokens}</div> Tokens (<div class="neutral_warning">${perm_tokens}</div><div> Permanent)</div>
|
||||||
</small>
|
</small>
|
||||||
|
<i title='Click for stats!' class="fa-solid fa-circle-info rm_stats_button"></i>
|
||||||
</div>
|
</div>
|
||||||
<div id="chartokenwarning" class="menu_button margin0 whitespacenowrap"><a href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#character-tokens" target="_blank">About Token 'Limits'</a></div>
|
<div id="chartokenwarning" class="menu_button margin0 whitespacenowrap"><a href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#character-tokens" target="_blank">About Token 'Limits'</a></div>
|
||||||
</div>`);
|
</div>`);
|
||||||
|
|
||||||
|
|
||||||
} //warn if either are over 1024
|
} //warn if either are over 1024
|
||||||
|
$(".rm_stats_button").on('click', function () {
|
||||||
|
characterStatsHandler(charStats, characters, this_chid);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
//Auto Load Last Charcter -- (fires when active_character is defined and auto_load_chat is true)
|
//Auto Load Last Charcter -- (fires when active_character is defined and auto_load_chat is true)
|
||||||
async function RA_autoloadchat() {
|
async function RA_autoloadchat() {
|
||||||
|
@@ -1223,7 +1223,7 @@ function openCharacterDefinition(characterSelect) {
|
|||||||
select_selected_character(chid);
|
select_selected_character(chid);
|
||||||
// Gentle nudge to recalculate tokens
|
// Gentle nudge to recalculate tokens
|
||||||
RA_CountCharTokens();
|
RA_CountCharTokens();
|
||||||
AA_CountCharTime(chid);
|
//AA_CountCharTime(chid);
|
||||||
// Do a little tomfoolery to spoof the tag selector
|
// Do a little tomfoolery to spoof the tag selector
|
||||||
applyTagsOnCharacterSelect.call(characterSelect);
|
applyTagsOnCharacterSelect.call(characterSelect);
|
||||||
}
|
}
|
||||||
|
98
public/scripts/stats.js
Normal file
98
public/scripts/stats.js
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
// statsHelper.js
|
||||||
|
import { getRequestHeaders, callPopup, token } from '../script.js';
|
||||||
|
import { humanizeGenTime } from './RossAscends-mods.js';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Function for creating stat block HTML
|
||||||
|
function createStatBlock(statName, statValue) {
|
||||||
|
return `<div class="rm_stat_block">
|
||||||
|
<div class="rm_stat_name">${statName}:</div>
|
||||||
|
<div class="rm_stat_value">${statValue}</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateTotal(stat) {
|
||||||
|
return isNaN(stat) ? 0 : stat;
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateTotalStats(charStats) {
|
||||||
|
let totalStats = {
|
||||||
|
total_gen_time: 0,
|
||||||
|
user_msg_count: 0,
|
||||||
|
non_user_msg_count: 0,
|
||||||
|
user_word_count: 0,
|
||||||
|
non_user_word_count: 0,
|
||||||
|
total_swipe_count: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let stats of Object.values(charStats)) {
|
||||||
|
totalStats.total_gen_time += calculateTotal(stats.total_gen_time);
|
||||||
|
totalStats.user_msg_count += calculateTotal(stats.user_msg_count);
|
||||||
|
totalStats.non_user_msg_count += calculateTotal(stats.non_user_msg_count);
|
||||||
|
totalStats.user_word_count += calculateTotal(stats.user_word_count);
|
||||||
|
totalStats.non_user_word_count += calculateTotal(stats.non_user_word_count);
|
||||||
|
totalStats.total_swipe_count += calculateTotal(stats.total_swipe_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalStats;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createHtml(statsType, stats) {
|
||||||
|
// Get time string
|
||||||
|
let timeStirng = humanizeGenTime(stats.total_gen_time);
|
||||||
|
|
||||||
|
// Create popup HTML with stats
|
||||||
|
let html = `<h3>${statsType} Stats</h3>`;
|
||||||
|
html += createStatBlock('Chat Time', timeStirng);
|
||||||
|
html += createStatBlock('Total User Messages', stats.user_msg_count);
|
||||||
|
html += createStatBlock('Total Character Messages', stats.non_user_msg_count);
|
||||||
|
html += createStatBlock('Total User Words', stats.user_word_count);
|
||||||
|
html += createStatBlock('Total Character Words', stats.non_user_word_count);
|
||||||
|
html += createStatBlock('Swipes', stats.total_swipe_count);
|
||||||
|
|
||||||
|
callPopup(html, 'text');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function userStatsHandler(charStats) {
|
||||||
|
// Get stats from server
|
||||||
|
let stats = await getStats(charStats);
|
||||||
|
|
||||||
|
// Calculate total stats
|
||||||
|
let totalStats = calculateTotalStats(stats);
|
||||||
|
console.log(totalStats);
|
||||||
|
|
||||||
|
// Create HTML with stats
|
||||||
|
createHtml('User', totalStats);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function characterStatsHandler(charStats, characters, this_chid) {
|
||||||
|
// Get stats from server
|
||||||
|
let stats = await getStats(charStats);
|
||||||
|
|
||||||
|
// Get character stats
|
||||||
|
let myStats = stats[characters[this_chid].avatar];
|
||||||
|
|
||||||
|
// Create HTML with stats
|
||||||
|
createHtml('Character', myStats);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getStats(charStats) {
|
||||||
|
const response = await fetch("/getstats", {
|
||||||
|
method: "POST",
|
||||||
|
headers: getRequestHeaders(),
|
||||||
|
body: JSON.stringify({}),
|
||||||
|
cache: "no-cache",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
toastr.error('Stats could not be loaded. Try reloading the page.');
|
||||||
|
throw new Error('Error getting stats');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
return data;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export { userStatsHandler, characterStatsHandler, getStats };
|
@@ -1877,6 +1877,11 @@ grammarly-extension {
|
|||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.rm_stat_block {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
.large_dialogue_popup {
|
.large_dialogue_popup {
|
||||||
height: 90vh !important;
|
height: 90vh !important;
|
||||||
height: 90svh !important;
|
height: 90svh !important;
|
||||||
|
@@ -132,7 +132,6 @@ function countWordsInString(str) {
|
|||||||
* @return {object} An object containing the calculated statistics.
|
* @return {object} An object containing the calculated statistics.
|
||||||
*/
|
*/
|
||||||
const calculateStats = (chatsPath, item, index) => {
|
const calculateStats = (chatsPath, item, index) => {
|
||||||
console.log('Calculating stats for', item);
|
|
||||||
const char_dir = path.join(chatsPath, item.replace('.png', ''));
|
const char_dir = path.join(chatsPath, item.replace('.png', ''));
|
||||||
let chat_size = 0;
|
let chat_size = 0;
|
||||||
let date_last_chat = 0;
|
let date_last_chat = 0;
|
||||||
@@ -152,7 +151,6 @@ const calculateStats = (chatsPath, item, index) => {
|
|||||||
const chats = fs.readdirSync(char_dir);
|
const chats = fs.readdirSync(char_dir);
|
||||||
if (Array.isArray(chats) && chats.length) {
|
if (Array.isArray(chats) && chats.length) {
|
||||||
for (const chat of chats) {
|
for (const chat of chats) {
|
||||||
console.log(uniqueGenStartTimes);
|
|
||||||
const result = calculateTotalGenTimeAndWordCount(char_dir, chat, uniqueGenStartTimes);
|
const result = calculateTotalGenTimeAndWordCount(char_dir, chat, uniqueGenStartTimes);
|
||||||
stats.total_gen_time += result.totalGenTime || 0;
|
stats.total_gen_time += result.totalGenTime || 0;
|
||||||
stats.user_word_count += result.userWordCount || 0;
|
stats.user_word_count += result.userWordCount || 0;
|
||||||
@@ -164,7 +162,6 @@ const calculateStats = (chatsPath, item, index) => {
|
|||||||
const chatStat = fs.statSync(path.join(char_dir, chat));
|
const chatStat = fs.statSync(path.join(char_dir, chat));
|
||||||
stats.chat_size += chatStat.size;
|
stats.chat_size += chatStat.size;
|
||||||
stats.date_last_chat = Math.max(stats.date_last_chat, chatStat.mtimeMs);
|
stats.date_last_chat = Math.max(stats.date_last_chat, chatStat.mtimeMs);
|
||||||
console.log(stats);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user