Repository navigation
Expand file tree
/
Copy pathblend-model-pricing.user.js
More file actions
113 lines (90 loc) · 3.6 KB
/
Copy pathblend-model-pricing.user.js
File metadata and controls
113 lines (90 loc) · 3.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// ==UserScript==
// @name Blend Model Pricing
// @namespace shiftgeist
// @icon https://www.google.com/s2/favicons?sz=64&domain=models.dev
// @version 0.0.2
//
// @match https://models.dev/*
// @grant none
// @run-at document-idle
//
// @author shiftgeist
// @description Show the combined pricing of input and output tokens per 1m
// @license GNU GPLv3
//
// @homepage https://github.com/shiftgeist/userscripts
// @updateURL https://raw.githubusercontent.com/shiftgeist/userscripts/refs/heads/main/models.dev/blend-model-pricing.user.js
// @downloadURL https://raw.githubusercontent.com/shiftgeist/userscripts/refs/heads/main/models.dev/blend-model-pricing.user.js
// @supportURL https://github.com/shiftgeist/userscripts/issues
// ==/UserScript==
;(() => {
'use strict'
const debug = window.localStorage.getItem('debug') === 'true'
const $ = (selector, parent = document) => parent.querySelector(selector)
const $$ = (selector, parent = document) => [...parent.querySelectorAll(selector)]
function log(...params) {
if (debug) console.debug('[Blend]', ...params)
}
function createElement(tagName, properties = {}, children = []) {
const element = document.createElement(tagName)
Object.assign(element, properties)
element.append(...children)
return element
}
function waitForElement(selector, callback) {
const existing = $(selector)
if (existing) return callback(existing)
const observer = new MutationObserver(() => {
const element = $(selector)
if (!element) return
observer.disconnect()
callback(element)
})
observer.observe(document.documentElement, { childList: true, subtree: true })
}
const usdFormatter = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' })
const currency = {
format: (amount) => usdFormatter.format(amount),
parse: (text) => Number(text.replace(/[^0-9.-]/g, ''))
}
function parsePrices(prices) {
return prices.split('/').map(v => v.trim()).map(currency.parse)
}
function blend([inputPrice, outputPrice] = [2, 10],
[inputTokens, outputTokens] = [1_000_000, 1_000_000])
{
const totalTokens = inputTokens + outputTokens
if (totalTokens === 0) return 0
const inputShare = inputTokens / totalTokens
const outputShare = outputTokens / totalTokens
return inputPrice * inputShare + outputPrice * outputShare
}
function main(table) {
log('Target element found:', table)
const thCells = $$('thead > tr > th', table)
log('thCells', thCells)
const priceCellTh = thCells.find(v => v.innerText.trim().toLowerCase() === 'price')
log('priceCellTh', priceCellTh, thCells.indexOf(priceCellTh))
const blendTh = createElement('th', { scope: 'col', innerText: 'Blend' })
blendTh.className = 'sortable'
blendTh.dataset.type = 'number'
priceCellTh.after(blendTh)
blendTh.append(priceCellTh.childNodes[1].cloneNode())
const priceCells = $$(`tbody > tr > td:nth-child(${thCells.indexOf(priceCellTh) + 1})`, table)
log('priceCells', priceCells)
priceCells.forEach(cell => {
const parsed = parsePrices(cell.innerText)
log('parsed', parsed)
const blended = blend(parsed)
log('combined', blended)
const format = currency.format(blended)
log('format', format)
const blendTd = createElement('td', { innerText: format })
blendTd.dataset.sort = `${blended.toFixed(2)}`
cell.dataset.originalSort = cell.dataset.sort
cell.dataset.sort = `${blended.toFixed(2)}`
cell.after(blendTd)
})
}
waitForElement('.table-wrap > table', main)
})()