import React, { useState, useEffect, useMemo } from "react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Switch } from "@/components/ui/switch"; import { mockData } from "@/lib/data"; import { ComparisonSelector } from "@/components/ComparisonSelector"; import { PricingTable } from "@/components/PricingTable"; import { BenchmarkTable } from "./components/BenchmarkTable"; import { benchmarkData } from "./lib/benchmarks/ index"; import { BenchmarkComparisonSelector } from "./components/BenchmarkComparisonSelector"; export interface FlattenedModel extends Model { provider: string; uri: string; benchmark?: { [key: string]: number; }; } export interface Model { name: string; inputPrice: number; outputPrice: number; } export interface Provider { provider: string; uri: string; models: Model[]; } const App: React.FC = () => { const [data, setData] = useState([]); const [comparisonModels, setComparisonModels] = useState([]); const [inputTokens, setInputTokens] = useState(1); const [outputTokens, setOutputTokens] = useState(1); const [selectedProviders, setSelectedProviders] = useState([]); const [selectedModels, setSelectedModels] = useState([]); const [expandedProviders, setExpandedProviders] = useState([]); const [tokenCalculation, setTokenCalculation] = useState("million"); const [linkProviderModel, setLinkProviderModel] = useState(false); const [benchmarkComparisonMetrics, setBenchmarkComparisonMetrics] = useState([]); const [selectedBenchmarkProviders, setSelectedBenchmarkProviders] = useState([]); const [selectedBenchmarkModels, setSelectedBenchmarkModels] = useState([]); const [linkBenchmarkProviderModel, setLinkBenchmarkProviderModel] = useState(false); const [sortConfig, setSortConfig] = useState<{ key: keyof FlattenedModel; direction: string; } | null>(null); const [benchmarkSortConfig, setBenchmarkSortConfig] = useState<{ key: string; direction: "ascending" | "descending"; } | null>(null); useEffect(() => { setData(mockData); }, []); const flattenDataFromPricing = (data: Provider[]): FlattenedModel[] => data.flatMap((provider) => provider.models.map((model) => ({ provider: provider.provider, uri: provider.uri, ...model, benchmark: {}, })) ); const flattenDataFromBenchmarks = (): FlattenedModel[] => benchmarkData.map((b) => ({ provider: b.provider ?? "Unknown", uri: b.source, // placeholder or use an optional `b.link` if available name: b.model, inputPrice: b.inputPrice, outputPrice: b.outputPrice, benchmark: b.benchmark ?? {}, })); const filteredData = useMemo(() => { if (!selectedProviders.length && !selectedModels.length && !linkProviderModel) return data; return data .filter((p) => !selectedProviders.length || selectedProviders.includes(p.provider)) .map((p) => ({ ...p, models: p.models.filter((m) => { if (linkProviderModel && !selectedModels.length) return selectedProviders.includes(p.provider); if (!linkProviderModel && !selectedModels.length) return !selectedProviders.length || selectedProviders.includes(p.provider); return selectedModels.includes(m.name); }), })) .filter((p) => p.models.length > 0); }, [data, selectedProviders, selectedModels, linkProviderModel]); const benchmarkedModels = useMemo(() => { return flattenDataFromBenchmarks(); }, []); const filteredBenchmarkedModels = useMemo(() => { return benchmarkedModels.filter((model) => { const providerMatch = selectedBenchmarkProviders.length === 0 || selectedBenchmarkProviders.includes(model.provider); const modelMatch = selectedBenchmarkModels.length === 0 || selectedBenchmarkModels.includes(model.name); if (linkBenchmarkProviderModel) { return providerMatch && modelMatch; } // When not linking, allow filtering by either if (selectedBenchmarkProviders.length > 0 && selectedBenchmarkModels.length > 0) { return providerMatch || modelMatch; } return providerMatch && modelMatch; // this handles the case where one or both are empty }); }, [ benchmarkedModels, selectedBenchmarkProviders, selectedBenchmarkModels, linkBenchmarkProviderModel, ]); const sortedBenchmarkedModels = useMemo(() => { if (!benchmarkSortConfig) return filteredBenchmarkedModels; return [...benchmarkedModels].sort((a, b) => { const key = benchmarkSortConfig.key; const aVal = key === "provider" || key === "name" ? (a as any)[key]?.toLowerCase?.() ?? "" : a.benchmark?.[key] ?? -Infinity; const bVal = key === "provider" || key === "name" ? (b as any)[key]?.toLowerCase?.() ?? "" : b.benchmark?.[key] ?? -Infinity; if (typeof aVal === "string" && typeof bVal === "string") { return benchmarkSortConfig.direction === "ascending" ? aVal.localeCompare(bVal) : bVal.localeCompare(aVal); } return benchmarkSortConfig.direction === "ascending" ? aVal - bVal : bVal - aVal; }); }, [filteredBenchmarkedModels, benchmarkSortConfig]); const benchmarkProviders = useMemo(() => { const modelsInBenchmark = new Set( benchmarkedModels.map((m) => `${m.provider}:${m.name}`) ); return data .map((provider) => ({ ...provider, models: provider.models.filter((model) => modelsInBenchmark.has(`${provider.provider}:${model.name}`) ), })) .filter((p) => p.models.length > 0); }, [data, benchmarkedModels]); const sortedFlattenedData = useMemo(() => { const flattened = flattenDataFromPricing(filteredData); if (!sortConfig) return flattened; return [...flattened].sort((a, b) => { const aVal = a[sortConfig.key]; const bVal = b[sortConfig.key]; if (typeof aVal === "string" && typeof bVal === "string") { return sortConfig.direction === "ascending" ? aVal.localeCompare(bVal) : bVal.localeCompare(aVal); } else if (typeof aVal === "number" && typeof bVal === "number") { return sortConfig.direction === "ascending" ? aVal - bVal : bVal - aVal; } return 0; }); }, [filteredData, sortConfig]); const requestSort = (key: keyof FlattenedModel) => { const direction = sortConfig?.key === key && sortConfig.direction === "ascending" ? "descending" : "ascending"; setSortConfig({ key, direction }); }; const toggleProviderExpansion = (provider: string) => { setExpandedProviders((prev) => prev.includes(provider) ? prev.filter((p) => p !== provider) : [...prev, provider] ); }; return ( LLM Pricing Calculator {/* Source Link */}

This is a fork of philschmid tool: philschmid/llm-pricing

{/* Comparison Model Selector */}

Select Comparison Models

setComparisonModels((prev) => checked ? [...prev, modelId] : prev.filter((m) => m !== modelId) ) } /> {/* Token Inputs */}
setInputTokens(Number(e.target.value))} />
setOutputTokens(Number(e.target.value))} />
{/* Provider–Model Toggle */}
{/* Pricing Table */}

Pricing Table

{/* Benchmark Table */}

Select Benchmark Metrics to Compare

Object.keys(m.benchmark ?? {})))).sort()} selected={benchmarkComparisonMetrics} onChange={(metric, checked) => setBenchmarkComparisonMetrics((prev) => checked ? [...prev, metric] : prev.filter((m) => m !== metric) ) } />

Benchmark Table

{ setBenchmarkSortConfig((prev) => prev?.key === key ? { key, direction: prev.direction === "ascending" ? "descending" : "ascending" } : { key, direction: "descending" } ); }} sortConfig={benchmarkSortConfig} allProviders={benchmarkProviders} selectedProviders={selectedBenchmarkProviders} selectedModels={selectedBenchmarkModels} onProviderChange={setSelectedBenchmarkProviders} onModelChange={setSelectedBenchmarkModels} linkProviderModel={linkBenchmarkProviderModel} onLinkToggle={setLinkBenchmarkProviderModel} />
); }; export default App;