From 9eb3e12da207aef97d2e11e43a4b59c09bfbb623 Mon Sep 17 00:00:00 2001 From: Darren Reid Date: Wed, 10 Apr 2024 14:01:13 +1000 Subject: [PATCH 1/9] Save leaderboard stats to json file, read from json file for page view. --- MyApp.ServiceInterface/LeaderboardServices.cs | 6 +++++- MyApp/Components/Pages/Leaderboard.razor | 9 +++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/MyApp.ServiceInterface/LeaderboardServices.cs b/MyApp.ServiceInterface/LeaderboardServices.cs index 4a48ad5..860a71a 100644 --- a/MyApp.ServiceInterface/LeaderboardServices.cs +++ b/MyApp.ServiceInterface/LeaderboardServices.cs @@ -38,7 +38,11 @@ public async Task Any(CalculateLeaderBoard request) }).ToList(); var leaderBoard = CalculateLeaderboardResponse(statsByUser, answers); - + + // Serialize the response to a leaderboard json file + var json = leaderBoard.ToJson(); + await File.WriteAllTextAsync("leaderboard.json", json); + return leaderBoard; } diff --git a/MyApp/Components/Pages/Leaderboard.razor b/MyApp/Components/Pages/Leaderboard.razor index 5b7d529..d58c655 100644 --- a/MyApp/Components/Pages/Leaderboard.razor +++ b/MyApp/Components/Pages/Leaderboard.razor @@ -1,5 +1,6 @@ @page "/leaderboard" @using MyApp.ServiceInterface +@using ServiceStack.Text @inherits AppComponentBase Leaderboard @@ -23,12 +24,8 @@ protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); - var api = await ApiAsync(new CalculateLeaderBoard()); - if (api.Succeeded) - data = api.Response; - else - Console.WriteLine(api.Error?.Message); - + var jsonData = File.ReadAllText("leaderboard.json"); + data = jsonData.FromJson(); } } From d3d7d89ad35afbc2d937bcaa42381df9dfc5e0a8 Mon Sep 17 00:00:00 2001 From: Darren Reid Date: Wed, 10 Apr 2024 14:03:28 +1000 Subject: [PATCH 2/9] Generate leaderboard if required. --- MyApp/Components/Pages/Leaderboard.razor | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MyApp/Components/Pages/Leaderboard.razor b/MyApp/Components/Pages/Leaderboard.razor index d58c655..f2b1b5e 100644 --- a/MyApp/Components/Pages/Leaderboard.razor +++ b/MyApp/Components/Pages/Leaderboard.razor @@ -24,6 +24,9 @@ protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); + // Only generate if missing, otherwise background task will update it. + if (!File.Exists("leaderboard.json")) + await ApiAsync(new CalculateLeaderBoard()); var jsonData = File.ReadAllText("leaderboard.json"); data = jsonData.FromJson(); } From a1dc1716e35a8fa6cbe250d4bdb860a2c151be7e Mon Sep 17 00:00:00 2001 From: Darren Reid Date: Wed, 10 Apr 2024 14:06:26 +1000 Subject: [PATCH 3/9] Change path to deal with permissions. --- MyApp.ServiceInterface/LeaderboardServices.cs | 2 +- MyApp/Components/Pages/Leaderboard.razor | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MyApp.ServiceInterface/LeaderboardServices.cs b/MyApp.ServiceInterface/LeaderboardServices.cs index 860a71a..b194d87 100644 --- a/MyApp.ServiceInterface/LeaderboardServices.cs +++ b/MyApp.ServiceInterface/LeaderboardServices.cs @@ -41,7 +41,7 @@ public async Task Any(CalculateLeaderBoard request) // Serialize the response to a leaderboard json file var json = leaderBoard.ToJson(); - await File.WriteAllTextAsync("leaderboard.json", json); + await File.WriteAllTextAsync("App_Data/leaderboard.json", json); return leaderBoard; } diff --git a/MyApp/Components/Pages/Leaderboard.razor b/MyApp/Components/Pages/Leaderboard.razor index f2b1b5e..c44f37c 100644 --- a/MyApp/Components/Pages/Leaderboard.razor +++ b/MyApp/Components/Pages/Leaderboard.razor @@ -25,9 +25,9 @@ { await base.OnInitializedAsync(); // Only generate if missing, otherwise background task will update it. - if (!File.Exists("leaderboard.json")) + if (!File.Exists("App_Data/leaderboard.json")) await ApiAsync(new CalculateLeaderBoard()); - var jsonData = File.ReadAllText("leaderboard.json"); + var jsonData = File.ReadAllText("App_Data/leaderboard.json"); data = jsonData.FromJson(); } From 4c1b8eb3dcf2fe7217434d6ae2278af25fc984d2 Mon Sep 17 00:00:00 2001 From: Darren Reid Date: Wed, 10 Apr 2024 14:15:31 +1000 Subject: [PATCH 4/9] Format and style changes for LeaderboardView.razor --- MyApp/Components/Shared/LeaderboardView.razor | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MyApp/Components/Shared/LeaderboardView.razor b/MyApp/Components/Shared/LeaderboardView.razor index 1b287b4..c361f2d 100644 --- a/MyApp/Components/Shared/LeaderboardView.razor +++ b/MyApp/Components/Shared/LeaderboardView.razor @@ -11,10 +11,10 @@ var displayName = AppConfig.GetApplicationUser(model.Id).DisplayName; var avatarUrl = userName.GetAvatarUrl();
- Avatar + Avatar

@displayName

-
@model.StartingUpVotes votes
+
@model.StartingUpVotes.ToHumanReadable() votes
} @@ -31,7 +31,7 @@ var avatarUrl = userName.GetAvatarUrl();
- Avatar + Avatar

@displayName

@(Math.Round(model.WinRate,2)) %
From 9496c5a35d27f524f2fc2d296c220739e9e8e245 Mon Sep 17 00:00:00 2001 From: Darren Reid Date: Wed, 10 Apr 2024 14:17:20 +1000 Subject: [PATCH 5/9] Add output cache to Leaderboard.razor --- MyApp/Components/Pages/Leaderboard.razor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MyApp/Components/Pages/Leaderboard.razor b/MyApp/Components/Pages/Leaderboard.razor index c44f37c..d843ade 100644 --- a/MyApp/Components/Pages/Leaderboard.razor +++ b/MyApp/Components/Pages/Leaderboard.razor @@ -1,6 +1,6 @@ @page "/leaderboard" @using MyApp.ServiceInterface -@using ServiceStack.Text +@attribute [OutputCache(Duration = 3600)] @inherits AppComponentBase Leaderboard From f0f7a57630faf742fd91941867d9b084168877dc Mon Sep 17 00:00:00 2001 From: Darren Reid Date: Wed, 10 Apr 2024 14:26:47 +1000 Subject: [PATCH 6/9] Numbered ranks on the left. --- MyApp/Components/Shared/LeaderboardView.razor | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/MyApp/Components/Shared/LeaderboardView.razor b/MyApp/Components/Shared/LeaderboardView.razor index c361f2d..b48494c 100644 --- a/MyApp/Components/Shared/LeaderboardView.razor +++ b/MyApp/Components/Shared/LeaderboardView.razor @@ -5,18 +5,24 @@

Total Votes

+ @{ + int rankTotal = 1; + } @foreach (var model in LeaderBoardData.MostLikedModelsByLlm.OrderByDescending(x => x.StartingUpVotes)) { var userName = AppConfig.GetUserName(model.Id); var displayName = AppConfig.GetApplicationUser(model.Id).DisplayName; var avatarUrl = userName.GetAvatarUrl();
- Avatar +
@rankTotal .
+ Avatar

@displayName

@model.StartingUpVotes.ToHumanReadable() votes
+ + rankTotal++; }
@@ -24,6 +30,9 @@

Win Rate

+ @{ + rankTotal = 1; + } @foreach (var model in LeaderBoardData.ModelWinRate.OrderByDescending(x => x.WinRate)) { var userName = AppConfig.GetUserName(model.Id); @@ -31,12 +40,15 @@ var avatarUrl = userName.GetAvatarUrl();
- Avatar +
@rankTotal .
+ Avatar

@displayName

-
@(Math.Round(model.WinRate,2)) %
+
@(Math.Round(model.WinRate, 2)) %
+ + rankTotal++; }
From 7c0cd04b0ec86586dbd6e485486ee1bea689da08 Mon Sep 17 00:00:00 2001 From: Darren Reid Date: Wed, 10 Apr 2024 14:27:44 +1000 Subject: [PATCH 7/9] Fix styles. --- MyApp/Components/Shared/LeaderboardView.razor | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MyApp/Components/Shared/LeaderboardView.razor b/MyApp/Components/Shared/LeaderboardView.razor index b48494c..f38f3c8 100644 --- a/MyApp/Components/Shared/LeaderboardView.razor +++ b/MyApp/Components/Shared/LeaderboardView.razor @@ -14,7 +14,7 @@ var displayName = AppConfig.GetApplicationUser(model.Id).DisplayName; var avatarUrl = userName.GetAvatarUrl();
-
@rankTotal .
+
@rankTotal .
Avatar

@displayName

@@ -40,7 +40,7 @@ var avatarUrl = userName.GetAvatarUrl();
-
@rankTotal .
+
@rankTotal .
Avatar

@displayName

From 11cc3b2c4f7da0317d66487e93d2d3f9c7a04d6e Mon Sep 17 00:00:00 2001 From: Darren Reid Date: Wed, 10 Apr 2024 15:41:14 +1000 Subject: [PATCH 8/9] First pass at win rate chartjs integration. --- MyApp/Components/Shared/LeaderboardView.razor | 14 +++++++++ MyApp/wwwroot/mjs/leaderboard.mjs | 30 +++++++++++++++++++ MyApp/wwwroot/posts/components/ChartJs.mjs | 2 +- 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 MyApp/wwwroot/mjs/leaderboard.mjs diff --git a/MyApp/Components/Shared/LeaderboardView.razor b/MyApp/Components/Shared/LeaderboardView.razor index f38f3c8..b4ab101 100644 --- a/MyApp/Components/Shared/LeaderboardView.razor +++ b/MyApp/Components/Shared/LeaderboardView.razor @@ -54,6 +54,20 @@
+
+ Total Votes. Based on total number of votes received by each model by an LLM comparing and scoring each answer compared to the associated question. +
+
+ Win Rate. Calculated win rate of each model based on their participation in questions where they received votes. +
+ +
+ +
+ @code { [Parameter] public CalculateLeaderboardResponse LeaderBoardData { get; set; } diff --git a/MyApp/wwwroot/mjs/leaderboard.mjs b/MyApp/wwwroot/mjs/leaderboard.mjs new file mode 100644 index 0000000..9bf5069 --- /dev/null +++ b/MyApp/wwwroot/mjs/leaderboard.mjs @@ -0,0 +1,30 @@ +import { ref, computed, watch, watchEffect, nextTick, onMounted, onUpdated, getCurrentInstance } from "vue" +import { $$, $1, on, JsonServiceClient, EventBus, toDate, humanize, leftPart, lastRightPart } from "@servicestack/client" +import { useClient, useAuth, useUtils } from "@servicestack/vue" +import { mount, alreadyMounted, forceMount } from "app.mjs" +import {GetMeta} from "./dtos.mjs"; +import Chart from "../posts/components/ChartJs.mjs"; + +export default { + async load() { + const el = $1("#winrate-chart") + if (!el) return + + // Get data prop values from data attributes + const labelsArray = el.getAttribute("data-labels") + const datasetsArray = el.getAttribute("data-datasets") + const optionsObj = el.getAttribute("data-options") + + // Get type prop value from data attribute + const type = "bar" + + console.log({labelsArray, datasetsArray, optionsObj, type}) + + // Parse data attributes + const labels = JSON.parse(labelsArray) + const datasets = JSON.parse(datasetsArray) + const options = JSON.parse(optionsObj) + + mount(el, Chart, { type: type, data: {labels: labels, datasets: datasets}, options: options }) + } +} \ No newline at end of file diff --git a/MyApp/wwwroot/posts/components/ChartJs.mjs b/MyApp/wwwroot/posts/components/ChartJs.mjs index a0c5a28..6e77d0c 100644 --- a/MyApp/wwwroot/posts/components/ChartJs.mjs +++ b/MyApp/wwwroot/posts/components/ChartJs.mjs @@ -1,7 +1,7 @@ import { ref, onMounted } from "vue" import { addScript } from "@servicestack/client" -const loadJs = addScript('https://cdn.jsdelivr.net/npm/chart.js/dist/chart.umd.min.js') +const loadJs = addScript('/lib/js/chart.umd.min.js') export default { template:`
`, From 85a3f14edb8999c169f0e05f30f568b3d549dfda Mon Sep 17 00:00:00 2001 From: Darren Reid Date: Wed, 10 Apr 2024 17:02:37 +1000 Subject: [PATCH 9/9] leaderboard stats fixes. style fixes first pass at barchart --- MyApp.ServiceInterface/LeaderboardServices.cs | 29 ++++------- MyApp/Components/Shared/LeaderboardView.razor | 12 +++-- MyApp/wwwroot/mjs/leaderboard.mjs | 50 +++++++++++++++---- 3 files changed, 57 insertions(+), 34 deletions(-) diff --git a/MyApp.ServiceInterface/LeaderboardServices.cs b/MyApp.ServiceInterface/LeaderboardServices.cs index b194d87..0a8bf57 100644 --- a/MyApp.ServiceInterface/LeaderboardServices.cs +++ b/MyApp.ServiceInterface/LeaderboardServices.cs @@ -56,27 +56,12 @@ private CalculateLeaderboardResponse CalculateLeaderboardResponse(List x.Id.Contains("-" + y.Key)) }; return res; }).ToList(); - var modelScale = 1 / overallWinRates.Where(x => IsHuman(x.Id) == false) - .Sum(y => y.WinRate); - var humanScale = 1 / overallWinRates.Where(x => IsHuman(x.Id)) - .Sum(y => y.WinRate); - var skipScale = double.IsInfinity(modelScale); - if (skipScale) - { - modelScale = 1; - } - var skipHumanScale = double.IsInfinity(humanScale); - if (skipHumanScale) - { - humanScale = 1; - } - - var leaderBoard = new CalculateLeaderboardResponse { MostLikedModels = statsByUser.Where(x => IsHuman(x.Id) == false) @@ -100,13 +85,15 @@ private CalculateLeaderboardResponse CalculateLeaderboardResponse(List new LeaderBoardWinRate { Id = x.Id, - WinRate = x.WinRate * (skipHumanScale ? 1 : humanScale) * 100 + WinRate = x.WinRate * 100, + NumberOfQuestions = x.NumberOfQuestions }).ToList(), ModelWinRate = overallWinRates.Where(x => IsHuman(x.Id) == false) .Select(x => new ModelWinRate { Id = x.Id, - WinRate = x.WinRate * (skipScale ? 1 : modelScale) * 100 + WinRate = x.WinRate * 100, + NumberOfQuestions = x.NumberOfQuestions }).ToList(), ModelTotalScore = statsByUser.GroupBy(x => x.Id) .Select(x => new ModelTotalScore @@ -256,12 +243,16 @@ public class ModelWinRate { public string Id { get; set; } public double WinRate { get; set; } + + public int NumberOfQuestions { get; set; } } public class LeaderBoardWinRate { public string Id { get; set; } public double WinRate { get; set; } + + public int NumberOfQuestions { get; set; } } public class LeaderBoardWinRateByTag diff --git a/MyApp/Components/Shared/LeaderboardView.razor b/MyApp/Components/Shared/LeaderboardView.razor index b4ab101..8f906e5 100644 --- a/MyApp/Components/Shared/LeaderboardView.razor +++ b/MyApp/Components/Shared/LeaderboardView.razor @@ -15,7 +15,7 @@ var avatarUrl = userName.GetAvatarUrl();
@rankTotal .
- Avatar + Avatar

@displayName

@model.StartingUpVotes.ToHumanReadable() votes
@@ -41,10 +41,10 @@
@rankTotal .
- Avatar + Avatar

@displayName

-
@(Math.Round(model.WinRate, 2)) %
+
@(Math.Round(model.WinRate, 2)) % (@model.NumberOfQuestions)
@@ -62,8 +62,8 @@
-
@@ -71,4 +71,6 @@ @code { [Parameter] public CalculateLeaderboardResponse LeaderBoardData { get; set; } + + private List GetWinRates() => LeaderBoardData.ModelWinRate.OrderByDescending(x => x.WinRate).ToList(); } \ No newline at end of file diff --git a/MyApp/wwwroot/mjs/leaderboard.mjs b/MyApp/wwwroot/mjs/leaderboard.mjs index 9bf5069..973e09c 100644 --- a/MyApp/wwwroot/mjs/leaderboard.mjs +++ b/MyApp/wwwroot/mjs/leaderboard.mjs @@ -5,26 +5,56 @@ import { mount, alreadyMounted, forceMount } from "app.mjs" import {GetMeta} from "./dtos.mjs"; import Chart from "../posts/components/ChartJs.mjs"; -export default { +const modelColors = { + "gemma": "#1d4ed8", + "gemma-2b": "#60a5fa", + "mistral": "#b91c1c", + "gpt-4-turbo": "#047857", + "claude-3-opus": "#b45309", + "claude-3-haiku": "#44403c", + "claude-3-sonnet": "#78716c", +} + +const modelAliases = { + "gemma": "Gemini", + "gemma-2b": "Gemini 2B", + "mistral": "Mistral", + "gpt-4-turbo": "GPT-4 Turbo", + "claude-3-opus": "Claude 3 Opus", + "claude-3-haiku": "Claude 3 Haiku", + "claude-3-sonnet": "Claude 3 Sonnet", +} +export default { async load() { const el = $1("#winrate-chart") if (!el) return - + // Get data prop values from data attributes - const labelsArray = el.getAttribute("data-labels") const datasetsArray = el.getAttribute("data-datasets") const optionsObj = el.getAttribute("data-options") - + // Get type prop value from data attribute const type = "bar" - - console.log({labelsArray, datasetsArray, optionsObj, type}) - + // Parse data attributes - const labels = JSON.parse(labelsArray) const datasets = JSON.parse(datasetsArray) + const modelNames = datasets[0].modelNames + + // Create labels and data arrays based on the original order of modelNames + const labels = modelNames.map(modelName => modelAliases[modelName] || modelName); + const data = modelNames.map(modelName => datasets[0].data[modelNames.indexOf(modelName)]); + + // Create the dataset object + const newDataset = { + label: "Win Rate %", + data: data, + backgroundColor: modelNames.map(modelName => modelColors[modelName] || "#000"), + borderColor: modelNames.map(modelName => modelColors[modelName] || "#000"), + borderWidth: 1 + }; + const options = JSON.parse(optionsObj) - - mount(el, Chart, { type: type, data: {labels: labels, datasets: datasets}, options: options }) + + mount(el, Chart, { type: type, data: { labels: labels, datasets: [newDataset] }}) } } \ No newline at end of file