Commit
·
35a1853
1
Parent(s):
4060e49
- src/App.tsx +73 -16
- src/lib/data.ts +1 -1
src/App.tsx
CHANGED
|
@@ -29,10 +29,11 @@ const App: React.FC = () => {
|
|
| 29 |
const [selectedProviders, setSelectedProviders] = useState<string[]>([])
|
| 30 |
const [selectedModels, setSelectedModels] = useState<string[]>([])
|
| 31 |
const [expandedProviders, setExpandedProviders] = useState<string[]>([])
|
|
|
|
| 32 |
|
| 33 |
useEffect(() => {
|
| 34 |
setData(mockData)
|
| 35 |
-
setComparisonModels(['OpenAI:GPT-4o', 'Anthropic:Claude 3.5 (Sonnet)', 'Google:Gemini 1.5 Pro'])
|
| 36 |
}, [])
|
| 37 |
|
| 38 |
const calculatePrice = (price: number, tokens: number): number => {
|
|
@@ -50,6 +51,45 @@ const App: React.FC = () => {
|
|
| 50 |
}))
|
| 51 |
.filter((provider) => provider.models.length > 0)
|
| 52 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
console.log(filteredData)
|
| 54 |
|
| 55 |
const toggleProviderExpansion = (provider: string) => {
|
|
@@ -145,10 +185,26 @@ const App: React.FC = () => {
|
|
| 145 |
<Table>
|
| 146 |
<TableHeader>
|
| 147 |
<TableRow>
|
| 148 |
-
<TableHead>
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
<TableHead>Total Price</TableHead>
|
| 153 |
{comparisonModels.map((model) => (
|
| 154 |
<TableHead key={model} colSpan={2}>
|
|
@@ -191,7 +247,7 @@ const App: React.FC = () => {
|
|
| 191 |
</TableRow>
|
| 192 |
</TableHeader>
|
| 193 |
<TableBody>
|
| 194 |
-
{
|
| 195 |
provider.models.map((model) => (
|
| 196 |
<TableRow key={`${provider.provider}-${model.name}`}>
|
| 197 |
<TableCell>
|
|
@@ -217,13 +273,12 @@ const App: React.FC = () => {
|
|
| 217 |
return [
|
| 218 |
<TableCell
|
| 219 |
key={`${comparisonModel}-input`}
|
| 220 |
-
className={`${
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
: parseFloat(calculateComparison(model.inputPrice, comparisonModelData.inputPrice)) > 0
|
| 224 |
? 'bg-red-100'
|
| 225 |
: ''
|
| 226 |
-
|
| 227 |
>
|
| 228 |
{`${provider.provider}:${model.name}` === comparisonModel
|
| 229 |
? '0.00%'
|
|
@@ -231,13 +286,12 @@ const App: React.FC = () => {
|
|
| 231 |
</TableCell>,
|
| 232 |
<TableCell
|
| 233 |
key={`${comparisonModel}-output`}
|
| 234 |
-
className={`${
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
: parseFloat(calculateComparison(model.outputPrice, comparisonModelData.outputPrice)) > 0
|
| 238 |
? 'bg-red-100'
|
| 239 |
: ''
|
| 240 |
-
|
| 241 |
>
|
| 242 |
{`${provider.provider}:${model.name}` === comparisonModel
|
| 243 |
? '0.00%'
|
|
@@ -250,6 +304,9 @@ const App: React.FC = () => {
|
|
| 250 |
)}
|
| 251 |
</TableBody>
|
| 252 |
</Table>
|
|
|
|
|
|
|
|
|
|
| 253 |
</CardContent>
|
| 254 |
</Card>
|
| 255 |
)
|
|
|
|
| 29 |
const [selectedProviders, setSelectedProviders] = useState<string[]>([])
|
| 30 |
const [selectedModels, setSelectedModels] = useState<string[]>([])
|
| 31 |
const [expandedProviders, setExpandedProviders] = useState<string[]>([])
|
| 32 |
+
const [sortConfig, setSortConfig] = useState<{ key: string, direction: string } | null>(null)
|
| 33 |
|
| 34 |
useEffect(() => {
|
| 35 |
setData(mockData)
|
| 36 |
+
setComparisonModels(['OpenAI:GPT-4o-mini', 'Anthropic:Claude 3.5 (Sonnet)', 'Google:Gemini 1.5 Pro'])
|
| 37 |
}, [])
|
| 38 |
|
| 39 |
const calculatePrice = (price: number, tokens: number): number => {
|
|
|
|
| 51 |
}))
|
| 52 |
.filter((provider) => provider.models.length > 0)
|
| 53 |
|
| 54 |
+
const sortedData = React.useMemo(() => {
|
| 55 |
+
let sortableData = [...filteredData];
|
| 56 |
+
if (sortConfig !== null) {
|
| 57 |
+
if (sortConfig.key === 'provider') {
|
| 58 |
+
sortableData.sort((a, b) => {
|
| 59 |
+
if (a.provider < b.provider) {
|
| 60 |
+
return sortConfig.direction === 'ascending' ? -1 : 1;
|
| 61 |
+
}
|
| 62 |
+
if (a.provider > b.provider) {
|
| 63 |
+
return sortConfig.direction === 'ascending' ? 1 : -1;
|
| 64 |
+
}
|
| 65 |
+
return 0;
|
| 66 |
+
});
|
| 67 |
+
} else if (sortConfig.key === 'model' || sortConfig.key === 'inputPrice' || sortConfig.key === 'outputPrice') {
|
| 68 |
+
sortableData.forEach(provider => {
|
| 69 |
+
provider.models.sort((a, b) => {
|
| 70 |
+
if (a[sortConfig.key] < b[sortConfig.key]) {
|
| 71 |
+
return sortConfig.direction === 'ascending' ? -1 : 1;
|
| 72 |
+
}
|
| 73 |
+
if (a[sortConfig.key] > b[sortConfig.key]) {
|
| 74 |
+
return sortConfig.direction === 'ascending' ? 1 : -1;
|
| 75 |
+
}
|
| 76 |
+
return 0;
|
| 77 |
+
});
|
| 78 |
+
});
|
| 79 |
+
}
|
| 80 |
+
}
|
| 81 |
+
return sortableData;
|
| 82 |
+
}, [filteredData, sortConfig]);
|
| 83 |
+
|
| 84 |
+
const requestSort = (key: string) => {
|
| 85 |
+
let direction = 'ascending';
|
| 86 |
+
if (sortConfig && sortConfig.key === key && sortConfig.direction === 'ascending') {
|
| 87 |
+
direction = 'descending';
|
| 88 |
+
}
|
| 89 |
+
setSortConfig({ key, direction });
|
| 90 |
+
};
|
| 91 |
+
|
| 92 |
+
|
| 93 |
console.log(filteredData)
|
| 94 |
|
| 95 |
const toggleProviderExpansion = (provider: string) => {
|
|
|
|
| 185 |
<Table>
|
| 186 |
<TableHeader>
|
| 187 |
<TableRow>
|
| 188 |
+
<TableHead>
|
| 189 |
+
<button type="button" onClick={() => requestSort('provider')}>
|
| 190 |
+
Provider {sortConfig?.key === 'provider' ? (sortConfig.direction === 'ascending' ? '▲' : '▼') : null}
|
| 191 |
+
</button>
|
| 192 |
+
</TableHead>
|
| 193 |
+
<TableHead>
|
| 194 |
+
<button type="button" onClick={() => requestSort('model')}>
|
| 195 |
+
Model {sortConfig?.key === 'model' ? (sortConfig.direction === 'ascending' ? '▲' : '▼') : null}
|
| 196 |
+
</button>
|
| 197 |
+
</TableHead>
|
| 198 |
+
<TableHead>
|
| 199 |
+
<button type="button" onClick={() => requestSort('inputPrice')}>
|
| 200 |
+
Input Price (per 1M tokens) {sortConfig?.key === 'inputPrice' ? (sortConfig.direction === 'ascending' ? '▲' : '▼') : null}
|
| 201 |
+
</button>
|
| 202 |
+
</TableHead>
|
| 203 |
+
<TableHead>
|
| 204 |
+
<button type="button" onClick={() => requestSort('outputPrice')}>
|
| 205 |
+
Output Price (per 1M tokens) {sortConfig?.key === 'outputPrice' ? (sortConfig.direction === 'ascending' ? '▲' : '▼') : null}
|
| 206 |
+
</button>
|
| 207 |
+
</TableHead>
|
| 208 |
<TableHead>Total Price</TableHead>
|
| 209 |
{comparisonModels.map((model) => (
|
| 210 |
<TableHead key={model} colSpan={2}>
|
|
|
|
| 247 |
</TableRow>
|
| 248 |
</TableHeader>
|
| 249 |
<TableBody>
|
| 250 |
+
{sortedData.flatMap((provider) =>
|
| 251 |
provider.models.map((model) => (
|
| 252 |
<TableRow key={`${provider.provider}-${model.name}`}>
|
| 253 |
<TableCell>
|
|
|
|
| 273 |
return [
|
| 274 |
<TableCell
|
| 275 |
key={`${comparisonModel}-input`}
|
| 276 |
+
className={`${parseFloat(calculateComparison(model.inputPrice, comparisonModelData.inputPrice)) < 0
|
| 277 |
+
? 'bg-green-100'
|
| 278 |
+
: parseFloat(calculateComparison(model.inputPrice, comparisonModelData.inputPrice)) > 0
|
|
|
|
| 279 |
? 'bg-red-100'
|
| 280 |
: ''
|
| 281 |
+
}`}
|
| 282 |
>
|
| 283 |
{`${provider.provider}:${model.name}` === comparisonModel
|
| 284 |
? '0.00%'
|
|
|
|
| 286 |
</TableCell>,
|
| 287 |
<TableCell
|
| 288 |
key={`${comparisonModel}-output`}
|
| 289 |
+
className={`${parseFloat(calculateComparison(model.outputPrice, comparisonModelData.outputPrice)) < 0
|
| 290 |
+
? 'bg-green-100'
|
| 291 |
+
: parseFloat(calculateComparison(model.outputPrice, comparisonModelData.outputPrice)) > 0
|
|
|
|
| 292 |
? 'bg-red-100'
|
| 293 |
: ''
|
| 294 |
+
}`}
|
| 295 |
>
|
| 296 |
{`${provider.provider}:${model.name}` === comparisonModel
|
| 297 |
? '0.00%'
|
|
|
|
| 304 |
)}
|
| 305 |
</TableBody>
|
| 306 |
</Table>
|
| 307 |
+
|
| 308 |
+
|
| 309 |
+
|
| 310 |
</CardContent>
|
| 311 |
</Card>
|
| 312 |
)
|
src/lib/data.ts
CHANGED
|
@@ -6,7 +6,7 @@ export const mockData: Provider[] = [
|
|
| 6 |
uri: 'https://openai.com/api/pricing/',
|
| 7 |
models: [
|
| 8 |
{ name: 'GPT-4o', inputPrice: 5.0, outputPrice: 15.0 },
|
| 9 |
-
{ name: 'GPT-4o-mini', inputPrice: 0.15, outputPrice: 0.
|
| 10 |
{ name: 'GPT-4 (8K)', inputPrice: 30.0, outputPrice: 60.0 },
|
| 11 |
{ name: 'GPT-4 Turbo', inputPrice: 10.0, outputPrice: 30.0 },
|
| 12 |
{ name: 'GPT-3.5-turbo', inputPrice: 0.5, outputPrice: 1.5 },
|
|
|
|
| 6 |
uri: 'https://openai.com/api/pricing/',
|
| 7 |
models: [
|
| 8 |
{ name: 'GPT-4o', inputPrice: 5.0, outputPrice: 15.0 },
|
| 9 |
+
{ name: 'GPT-4o-mini', inputPrice: 0.15, outputPrice: 0.60 },
|
| 10 |
{ name: 'GPT-4 (8K)', inputPrice: 30.0, outputPrice: 60.0 },
|
| 11 |
{ name: 'GPT-4 Turbo', inputPrice: 10.0, outputPrice: 30.0 },
|
| 12 |
{ name: 'GPT-3.5-turbo', inputPrice: 0.5, outputPrice: 1.5 },
|