Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/mythz/pvq.app
Browse files Browse the repository at this point in the history
  • Loading branch information
mythz committed Apr 10, 2024
2 parents bc94b86 + 85a3f14 commit d1b5957
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 31 deletions.
35 changes: 15 additions & 20 deletions MyApp.ServiceInterface/LeaderboardServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@ public async Task<object> Any(CalculateLeaderBoard request)
}).ToList();

var leaderBoard = CalculateLeaderboardResponse(statsByUser, answers);


// Serialize the response to a leaderboard json file
var json = leaderBoard.ToJson();
await File.WriteAllTextAsync("App_Data/leaderboard.json", json);

return leaderBoard;
}

Expand All @@ -52,27 +56,12 @@ private CalculateLeaderboardResponse CalculateLeaderboardResponse(List<StatTotal
var res = new LeaderBoardWinRate
{
Id = y.Key,
WinRate = CalculateWinRate(answers, y.Key)
WinRate = CalculateWinRate(answers, y.Key),
NumberOfQuestions = answers.Count(x => 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)
Expand All @@ -96,13 +85,15 @@ private CalculateLeaderboardResponse CalculateLeaderboardResponse(List<StatTotal
.Select(x => 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
Expand Down Expand Up @@ -252,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
Expand Down
12 changes: 6 additions & 6 deletions MyApp/Components/Pages/Leaderboard.razor
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@page "/leaderboard"
@using MyApp.ServiceInterface
@attribute [OutputCache(Duration = 3600)]
@inherits AppComponentBase

<PageTitle>Leaderboard</PageTitle>
Expand All @@ -23,12 +24,11 @@
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);

// Only generate if missing, otherwise background task will update it.
if (!File.Exists("App_Data/leaderboard.json"))
await ApiAsync(new CalculateLeaderBoard());
var jsonData = File.ReadAllText("App_Data/leaderboard.json");
data = jsonData.FromJson<CalculateLeaderboardResponse>();
}

}
36 changes: 32 additions & 4 deletions MyApp/Components/Shared/LeaderboardView.razor
Original file line number Diff line number Diff line change
Expand Up @@ -5,44 +5,72 @@
<div class="bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-gray-100 p-12 rounded-lg shadow-lg">
<h2 class="text-2xl font-bold mb-4">Total Votes</h2>
<div class="space-y-4">
@{
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();
<div class="flex items-center">
<img src="@avatarUrl" alt="Avatar" class="w-24 h-24 rounded-full mr-4" />
<div class="mr-4 flex items-center justify-center w-12 h-8 text-black dark:text-white rounded-full text-2xl">@rankTotal .</div>
<img src="@avatarUrl" alt="Avatar" class="w-12 h-12 mr-4 space-y-6"/>
<div class="flex-1">
<h3 class="text-xl font-semibold">@displayName</h3>
<div class="text-md text-gray-600 dark:text-gray-400">@model.StartingUpVotes votes</div>
<div class="text-md text-gray-600 dark:text-gray-400">@model.StartingUpVotes.ToHumanReadable() votes</div>
</div>
</div>

rankTotal++;
}
</div>
</div>

<div class="bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-gray-100 p-12 rounded-lg shadow-lg">
<h2 class="text-2xl font-bold mb-4">Win Rate</h2>
<div class="space-y-4">
@{
rankTotal = 1;
}
@foreach (var model in LeaderBoardData.ModelWinRate.OrderByDescending(x => x.WinRate))
{
var userName = AppConfig.GetUserName(model.Id);
var displayName = AppConfig.GetApplicationUser(model.Id).DisplayName;
var avatarUrl = userName.GetAvatarUrl();

<div class="flex items-center">
<img src="@avatarUrl" alt="Avatar" class="w-24 h-24 rounded-full mr-4" />
<div class="mr-4 flex items-center justify-center w-12 h-8 text-black dark:text-white rounded-full text-2xl">@rankTotal .</div>
<img src="@avatarUrl" alt="Avatar" class="w-12 h-12 mr-4 space-y-6"/>
<div class="flex-1">
<h3 class="text-xl font-semibold">@displayName</h3>
<div class="text-md text-gray-600 dark:text-gray-400">@(Math.Round(model.WinRate,2)) %</div>
<div class="text-md text-gray-600 dark:text-gray-400">@(Math.Round(model.WinRate, 2)) % (@model.NumberOfQuestions)</div>
</div>
</div>

rankTotal++;
}
</div>
</div>
</div>

<div class="mt-8 text-gray-600 dark:text-gray-400">
<span class="font-bold">Total Votes</span>. Based on total number of votes received by each model by an LLM comparing and scoring each answer compared to the associated question.
</div>
<div class="mt-8 text-gray-600 dark:text-gray-400">
<span class="font-bold">Win Rate</span>. Calculated win rate of each model based on their participation in questions where they received votes.
</div>

<div data-module="mjs/leaderboard.mjs">
<chart-js id="winrate-chart" data-labels='[@string.Join(",", GetWinRates().Select(x => $"\"{AppConfig.GetApplicationUser(x.Id).DisplayName}\"") )]'
data-datasets='[{ "label": "Win Rate", "data": [@string.Join(",", GetWinRates().Select(x => Math.Round(x.WinRate, 2)))], "borderWidth": 1, "modelNames": @GetWinRates().Select(x => x.Id).ToJson() }]'
data-options='{ "scale": { "ticks": { "beginAtZero": true } }, "responsive": true, "maintainAspectRatio": false }'
data-type="pie"></chart-js>
</div>

@code {
[Parameter]
public CalculateLeaderboardResponse LeaderBoardData { get; set; }

private List<ModelWinRate> GetWinRates() => LeaderBoardData.ModelWinRate.OrderByDescending(x => x.WinRate).ToList();
}
60 changes: 60 additions & 0 deletions MyApp/wwwroot/mjs/leaderboard.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
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";

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 datasetsArray = el.getAttribute("data-datasets")
const optionsObj = el.getAttribute("data-options")

// Get type prop value from data attribute
const type = "bar"

// Parse data attributes
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: [newDataset] }})
}
}
2 changes: 1 addition & 1 deletion MyApp/wwwroot/posts/components/ChartJs.mjs
Original file line number Diff line number Diff line change
@@ -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:`<div><canvas ref="chart"></canvas></div>`,
Expand Down

0 comments on commit d1b5957

Please sign in to comment.