Presidentlin's picture
x
f23b928
raw
history blame
6.22 kB
import * as React from "react";
import {
Table,
TableHeader,
TableBody,
TableRow,
TableHead,
TableCell,
} from "@/components/ui/table";
import { MultiSelect } from "@/components/ui/multi-select";
import { FlattenedModel, Provider } from "@/App";
interface PricingTableProps {
data: FlattenedModel[];
providers: Provider[];
selectedProviders: string[];
selectedModels: string[];
onProviderChange: (values: string[]) => void;
onModelChange: (values: string[]) => void;
comparisonModels: string[];
inputTokens: number;
outputTokens: number;
tokenCalculation: string;
requestSort: (key: keyof FlattenedModel) => void;
sortConfig: {
key: keyof FlattenedModel;
direction: string;
} | null;
}
export const PricingTable: React.FC<PricingTableProps> = ({
data,
providers,
selectedProviders,
selectedModels,
onProviderChange,
onModelChange,
comparisonModels,
inputTokens,
outputTokens,
tokenCalculation,
requestSort,
sortConfig,
}) => {
const calculatePrice = (price: number, tokens: number): number => {
let multiplier = 1;
if (tokenCalculation === "thousand") multiplier = 1e-3;
else if (tokenCalculation === "unit") multiplier = 1e-6;
else if (tokenCalculation === "billion") multiplier = 1e3;
return price * tokens * multiplier;
};
const calculateComparison = (modelPrice: number, comparisonPrice: number): string => {
if (comparisonPrice === 0) return "∞";
return (((modelPrice - comparisonPrice) / comparisonPrice) * 100).toFixed(2);
};
const getModelsForSelectedProviders = () => {
const flatModels = providers.flatMap((provider) =>
provider.models.map((model) => ({
label: model.name,
value: model.name,
provider: provider.provider,
}))
);
const filtered = flatModels.filter((m) =>
!selectedProviders.length || selectedProviders.includes(m.provider)
);
// Remove duplicates
return Array.from(new Map(filtered.map((m) => [m.value, m])).values());
};
return (
<Table>
<TableHeader>
<TableRow>
<TableHead>
<button onClick={() => requestSort("provider")}>
Provider {sortConfig?.key === "provider" ? (sortConfig.direction === "ascending" ? "▲" : "▼") : null}
</button>
</TableHead>
<TableHead>
<button onClick={() => requestSort("name")}>
Model {sortConfig?.key === "name" ? (sortConfig.direction === "ascending" ? "▲" : "▼") : null}
</button>
</TableHead>
<TableHead>
<button onClick={() => requestSort("inputPrice")}>
Input Price (million tokens) {sortConfig?.key === "inputPrice" ? (sortConfig.direction === "ascending" ? "▲" : "▼") : null}
</button>
</TableHead>
<TableHead>
<button onClick={() => requestSort("outputPrice")}>
Output Price (million tokens) {sortConfig?.key === "outputPrice" ? (sortConfig.direction === "ascending" ? "▲" : "▼") : null}
</button>
</TableHead>
<TableHead>Total Price (per {tokenCalculation} tokens)</TableHead>
{comparisonModels.map((model) => (
<TableHead key={model} colSpan={2}>
Compared to {model}
</TableHead>
))}
</TableRow>
<TableRow>
<TableHead>
<MultiSelect
options={providers.map((p) => ({ label: p.provider, value: p.provider }))}
defaultValue={selectedProviders}
onValueChange={onProviderChange}
/>
</TableHead>
<TableHead>
<MultiSelect
options={getModelsForSelectedProviders()}
defaultValue={selectedModels}
onValueChange={onModelChange}
/>
</TableHead>
<TableHead />
<TableHead />
<TableHead />
{comparisonModels.flatMap((model) => [
<TableHead key={`${model}-input`}>Input</TableHead>,
<TableHead key={`${model}-output`}>Output</TableHead>,
])}
</TableRow>
</TableHeader>
<TableBody>
{data.map((item) => (
<TableRow key={`${item.provider}-${item.name}`}>
<TableCell>
<a href={item.uri} className="underline">
{item.provider}
</a>
</TableCell>
<TableCell>{item.name}</TableCell>
<TableCell>{item.inputPrice.toFixed(2)}</TableCell>
<TableCell>{item.outputPrice.toFixed(2)}</TableCell>
<TableCell className="font-bold">
${(
calculatePrice(item.inputPrice, inputTokens) +
calculatePrice(item.outputPrice, outputTokens)
).toFixed(2)}
</TableCell>
{comparisonModels.flatMap((model) => {
const [comparisonProvider, comparisonModelName] = model.split(":");
const comparisonModel = providers
.find((p) => p.provider === comparisonProvider)
?.models.find((m) => m.name === comparisonModelName);
if (!comparisonModel) return [<TableCell key="missing"></TableCell>, <TableCell key="missing2"></TableCell>];
const inputDelta = calculateComparison(item.inputPrice, comparisonModel.inputPrice);
const outputDelta = calculateComparison(item.outputPrice, comparisonModel.outputPrice);
return [
<TableCell key={`${model}-input`} className={parseFloat(inputDelta) < 0 ? "bg-green-100" : parseFloat(inputDelta) > 0 ? "bg-red-100" : ""}>
{`${item.provider}:${item.name}` === model ? "0.00%" : `${inputDelta}%`}
</TableCell>,
<TableCell key={`${model}-output`} className={parseFloat(outputDelta) < 0 ? "bg-green-100" : parseFloat(outputDelta) > 0 ? "bg-red-100" : ""}>
{`${item.provider}:${item.name}` === model ? "0.00%" : `${outputDelta}%`}
</TableCell>,
];
})}
</TableRow>
))}
</TableBody>
</Table>
);
};