Havre: sort the model picker by price #250
@@ -81,6 +81,41 @@ export const voicePrice = (model, chars) => (model && model.perMChars != null ?
|
||||
/** a minute of your voice, heard */
|
||||
export const hearingPerMinute = (model) => (model && model.perSecond != null ? model.perSecond * 60 : null);
|
||||
|
||||
/**
|
||||
* What a model costs, as the numbers the model picker sorts by, the first counting most: a text model's price in, then
|
||||
* out (a chat is mostly what's sent in: the card, the memories, the conversation so far); a photo's price, then what each
|
||||
* picture past the included ones adds; a voice's price for a million characters; hearing's price for a second. null when
|
||||
* Venice lists no price (a clip: it prices each one when asked).
|
||||
*/
|
||||
export function modelCost(type, model) {
|
||||
if (!model) return null;
|
||||
const n = (x) => (typeof x === "number" && Number.isFinite(x) ? x : null);
|
||||
const keys = type === "text" ? [n(model.input), n(model.output)]
|
||||
: type === "inpaint" || type === "image" ? [n(model.price), n(model.extra)]
|
||||
: type === "tts" ? [n(model.perMChars)]
|
||||
: type === "asr" ? [n(model.perSecond)]
|
||||
: [];
|
||||
return keys.length && keys[0] != null ? keys.map((k) => k ?? 0) : null;
|
||||
}
|
||||
/** the orders the model picker has: by name (the one in use first), cheapest first, priciest first */
|
||||
export const MODEL_SORTS = ["name", "cheapest", "priciest"];
|
||||
/**
|
||||
* The model picker's list in order, as a new list. "name": the ones rank puts higher first (the one in use, then the
|
||||
* character's own), then A to Z. "cheapest" and "priciest": by modelCost, A to Z between equals, the ones without a price
|
||||
* last either way, and none held at the top, so the one in use shows where its price puts it.
|
||||
*/
|
||||
export function sortModels(list, how = "name", { type = "text", rank = () => 0 } = {}) {
|
||||
const all = [...(list || [])], byName = (a, b) => String(a.name || a.id).localeCompare(String(b.name || b.id));
|
||||
if (how !== "cheapest" && how !== "priciest") return all.sort((a, b) => rank(b) - rank(a) || byName(a, b));
|
||||
const dir = how === "priciest" ? -1 : 1, cost = new Map(all.map((m) => [m, modelCost(type, m)]));
|
||||
return all.sort((a, b) => {
|
||||
const x = cost.get(a), y = cost.get(b);
|
||||
if (!x || !y) return (x ? 0 : 1) - (y ? 0 : 1) || byName(a, b);
|
||||
for (let i = 0; i < x.length; i++) if (x[i] !== y[i]) return dir * (x[i] - y[i]);
|
||||
return byName(a, b);
|
||||
});
|
||||
}
|
||||
|
||||
// ---------- what goes into a photo or a clip ----------
|
||||
|
||||
// Content filters (Venice's, and the ones of the models' makers) read the words in a prompt, not the "no" in front of
|
||||
|
||||
Reference in new issue
Block a user