|
1 | 1 | <script lang="ts"> |
| 2 | + import { onMount } from "svelte"; |
2 | 3 | import App from "./App.svelte"; |
3 | 4 | import Providers from "./Providers.svelte"; |
4 | 5 |
|
5 | 6 | let activeTab: "models" | "providers" = "models"; |
6 | 7 |
|
7 | 8 | const GITHUB_URL = "https://github.com/BerriAI/litellm"; |
8 | 9 | const DOCS_URL = "https://docs.litellm.ai"; |
| 10 | + const PROVIDERS_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/provider_endpoints_support.json"; |
| 11 | + const MODELS_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"; |
| 12 | +
|
| 13 | + let providerCount = 0; |
| 14 | + let endpointCount = 0; |
| 15 | + let providerEndpointCount = 0; |
| 16 | + let modelCount = 0; |
| 17 | + let statsLoading = true; |
| 18 | +
|
| 19 | + onMount(async () => { |
| 20 | + try { |
| 21 | + // Fetch provider data |
| 22 | + const providersResponse = await fetch(PROVIDERS_URL); |
| 23 | + const providersData = await providersResponse.json(); |
| 24 | + |
| 25 | + if (providersData.providers) { |
| 26 | + const providers = Object.entries(providersData.providers).map(([provider, info]: [string, any]) => ({ |
| 27 | + provider, |
| 28 | + endpoints: info.endpoints || {} |
| 29 | + })); |
| 30 | + |
| 31 | + providerCount = providers.length; |
| 32 | + |
| 33 | + // Count unique endpoints |
| 34 | + const allEndpoints = new Set<string>(); |
| 35 | + providers.forEach(p => { |
| 36 | + Object.keys(p.endpoints).forEach(e => allEndpoints.add(e)); |
| 37 | + }); |
| 38 | + endpointCount = allEndpoints.size; |
| 39 | + |
| 40 | + // Count provider + endpoint combinations |
| 41 | + providerEndpointCount = providers.reduce((total, provider) => { |
| 42 | + return total + Object.values(provider.endpoints).filter(supported => supported === true).length; |
| 43 | + }, 0); |
| 44 | + } |
| 45 | +
|
| 46 | + // Fetch model data |
| 47 | + const modelsResponse = await fetch(MODELS_URL); |
| 48 | + const modelsText = await modelsResponse.text(); |
| 49 | + const modelsData = JSON.parse(modelsText); |
| 50 | + modelCount = Object.keys(modelsData).length; |
| 51 | + |
| 52 | + statsLoading = false; |
| 53 | + } catch (error) { |
| 54 | + console.error("Failed to load statistics:", error); |
| 55 | + statsLoading = false; |
| 56 | + } |
| 57 | + }); |
9 | 58 | </script> |
10 | 59 |
|
11 | 60 | <div class="app-container"> |
|
41 | 90 | </div> |
42 | 91 | </header> |
43 | 92 |
|
| 93 | + <!-- Statistics Section --> |
| 94 | + {#if !statsLoading} |
| 95 | + <div class="stats-section"> |
| 96 | + <div class="stats-container"> |
| 97 | + <div class="stat-card"> |
| 98 | + <div class="stat-value">{modelCount}</div> |
| 99 | + <div class="stat-label">Models Supported</div> |
| 100 | + </div> |
| 101 | + <div class="stat-card"> |
| 102 | + <div class="stat-value">{providerCount}</div> |
| 103 | + <div class="stat-label">Providers</div> |
| 104 | + </div> |
| 105 | + <div class="stat-card"> |
| 106 | + <div class="stat-value">{endpointCount}</div> |
| 107 | + <div class="stat-label">Unique Endpoints</div> |
| 108 | + </div> |
| 109 | + <div class="stat-card"> |
| 110 | + <div class="stat-value">{providerEndpointCount}</div> |
| 111 | + <div class="stat-label">Provider + Endpoint Combinations</div> |
| 112 | + </div> |
| 113 | + </div> |
| 114 | + </div> |
| 115 | + {/if} |
| 116 | + |
44 | 117 | <!-- Content --> |
45 | 118 | {#if activeTab === "models"} |
46 | 119 | <App /> |
|
162 | 235 | .nav-link:hover { |
163 | 236 | color: var(--litellm-primary); |
164 | 237 | } |
| 238 | +
|
| 239 | + /* Statistics Section */ |
| 240 | + .stats-section { |
| 241 | + max-width: 1400px; |
| 242 | + margin: 1.5rem auto; |
| 243 | + padding: 0 2rem; |
| 244 | + } |
| 245 | +
|
| 246 | + .stats-container { |
| 247 | + display: grid; |
| 248 | + grid-template-columns: repeat(4, 1fr); |
| 249 | + gap: 0.75rem; |
| 250 | + } |
| 251 | +
|
| 252 | + .stat-card { |
| 253 | + background: #fcfcfc; |
| 254 | + border: 1px solid #f5f5f5; |
| 255 | + border-radius: 6px; |
| 256 | + padding: 0.875rem 0.75rem; |
| 257 | + text-align: center; |
| 258 | + transition: background-color 0.2s ease, border-color 0.2s ease; |
| 259 | + } |
| 260 | +
|
| 261 | + .stat-card:hover { |
| 262 | + background-color: #fafafa; |
| 263 | + border-color: #f0f0f0; |
| 264 | + } |
| 265 | +
|
| 266 | + .stat-value { |
| 267 | + font-size: 1.375rem; |
| 268 | + font-weight: 600; |
| 269 | + color: #1a1a1a; |
| 270 | + line-height: 1; |
| 271 | + margin-bottom: 0.25rem; |
| 272 | + } |
| 273 | +
|
| 274 | + .stat-label { |
| 275 | + font-size: 0.6875rem; |
| 276 | + font-weight: 500; |
| 277 | + color: #9ca3af; |
| 278 | + text-transform: uppercase; |
| 279 | + letter-spacing: 0.04em; |
| 280 | + } |
| 281 | +
|
| 282 | + /* Responsive Design */ |
| 283 | + @media (max-width: 1024px) { |
| 284 | + .stats-container { |
| 285 | + grid-template-columns: repeat(2, 1fr); |
| 286 | + } |
| 287 | + } |
| 288 | +
|
| 289 | + @media (max-width: 640px) { |
| 290 | + .stats-container { |
| 291 | + grid-template-columns: 1fr; |
| 292 | + gap: 0.75rem; |
| 293 | + } |
| 294 | +
|
| 295 | + .stat-card { |
| 296 | + padding: 1rem; |
| 297 | + } |
| 298 | +
|
| 299 | + .stat-value { |
| 300 | + font-size: 1.5rem; |
| 301 | + } |
| 302 | +
|
| 303 | + .stat-label { |
| 304 | + font-size: 0.6875rem; |
| 305 | + } |
| 306 | +
|
| 307 | + .left-section { |
| 308 | + flex-direction: column; |
| 309 | + align-items: flex-start; |
| 310 | + gap: 1rem; |
| 311 | + } |
| 312 | +
|
| 313 | + .tabs { |
| 314 | + width: 100%; |
| 315 | + flex-wrap: wrap; |
| 316 | + } |
| 317 | +
|
| 318 | + .tab { |
| 319 | + font-size: 0.875rem; |
| 320 | + padding: 0.5rem 0.75rem; |
| 321 | + } |
| 322 | + } |
165 | 323 | </style> |
166 | 324 |
|
0 commit comments