---
title: "List of Supported Models"
description: "Comprehensive catalog of supported AI models with detailed specifications, capabilities, and costs"
icon: "list"
mode: "wide"
---

export const ModelDialog = React.memo(({ model, onClose }) => {
  const modelString = `model: "${model.model || 'unknown'}"`
  const jsonString = JSON.stringify(model, null, 2)

  const copyModelString = useCallback(() => {
    navigator.clipboard.writeText(modelString)
  }, [modelString])

  const copyJson = useCallback(() => {
    navigator.clipboard.writeText(jsonString)
  }, [jsonString])

  return (
    <div
      className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50"
      onClick={onClose}
    >
      <div
        className="bg-white dark:bg-zinc-900 rounded-lg shadow-xl max-w-2xl w-full max-h-[80vh] overflow-hidden flex flex-col"
        onClick={(e) => e.stopPropagation()}
      >
        <div className="flex items-center justify-between p-4 border-b border-zinc-950/20 dark:border-white/20">
          <h3 className="text-lg font-semibold text-zinc-950 dark:text-white">
            Model Details: {model.model || 'Unknown'}
          </h3>
          <button
            onClick={onClose}
            className="p-2 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-lg transition-colors"
            aria-label="Close"
          >
            <svg className="w-5 h-5 text-zinc-950 dark:text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
            </svg>
          </button>
        </div>
        <div className="flex-1 overflow-auto p-4 space-y-4">
          <div className="bg-[#0C3B43]/10 dark:bg-[#07C983]/10 border border-[#0C3B43]/20 dark:border-[#07C983]/20 rounded-lg p-4">
            <div className="text-xs font-semibold text-[#0C3B43] dark:text-[#07C983] mb-2 uppercase tracking-wide">
              Use on Bifrost
            </div>
            <div className="flex items-center gap-2">
              <code className="flex-1 text-sm font-mono text-zinc-950 dark:text-white bg-white dark:bg-zinc-950 px-3 py-2 rounded border border-zinc-950/20 dark:border-white/20">
                {modelString}
              </code>
              <button
                onClick={copyModelString}
                className="p-2 text-sm font-medium text-[#0C3B43] dark:text-[#07C983] hover:bg-[#0C3B43]/10 dark:hover:bg-[#07C983]/10 rounded-lg transition-colors"
                title="Copy model string"
              >
                <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
                </svg>
              </button>
            </div>
          </div>
          
          <div>
            <div className="text-xs font-semibold text-zinc-950/70 dark:text-white/70 mb-2 uppercase tracking-wide">
              Full Configuration (JSON)
            </div>
            <pre className="text-sm text-zinc-950 dark:text-white bg-zinc-50 dark:bg-zinc-950 p-4 rounded-lg overflow-auto border border-zinc-950/20 dark:border-white/20">
              {jsonString}
            </pre>
          </div>
        </div>
        <div className="flex justify-end gap-2 p-4 border-t border-zinc-950/20 dark:border-white/20">
          <button
            onClick={copyJson}
            className="px-4 py-2 text-sm font-medium text-zinc-950 dark:text-white bg-zinc-100 dark:bg-zinc-800 hover:bg-zinc-200 dark:hover:bg-zinc-700 rounded-lg transition-colors"
          >
            Copy JSON
          </button>
          <button
            onClick={onClose}
            className="px-4 py-2 text-sm font-medium text-white bg-[#0C3B43] hover:bg-[#0a2f35] rounded-lg transition-colors"
          >
            Close
          </button>
        </div>
      </div>
    </div>
  )
})

export const ModelsCatalog = () => {
  const [models, setModels] = useState([])
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(null)
  const [searchTerm, setSearchTerm] = useState('')
  const [selectedProvider, setSelectedProvider] = useState('all')
  const [selectedModel, setSelectedModel] = useState(null)
  const [showDialog, setShowDialog] = useState(false)

  useEffect(() => {
    async function fetchModels () {
      try {
        const response = await fetch('https://getbifrost.ai/datasheet')
        if (!response.ok) {
          throw new Error(`Failed to fetch models: ${response.status}`)
        }
        const data = await response.json()
        
        // Convert object format {modelName: {config}} to array format
        if (data && typeof data === 'object' && !Array.isArray(data)) {
          const modelsArray = Object.entries(data).map(([modelName, config]) => ({
            model: modelName,
            ...config
          }))
          
          if (modelsArray.length === 0) {
            throw new Error('No models data received')
          }
          
          setModels(modelsArray)
        } else if (Array.isArray(data)) {
          setModels(data)
        } else {
          throw new Error('Invalid data format')
        }
      } catch (err) {
        console.error('Fetch error:', err)
        setError(err.message)
      } finally {
        setLoading(false)
      }
    }

    fetchModels()
  }, [])

  useEffect(() => {
    const styleId = 'custom-scrollbar-styles'
    if (document.getElementById(styleId)) return

    const style = document.createElement('style')
    style.id = styleId
    style.textContent = `
      /* Firefox overlay scrollbar - always visible */
      .custom-scrollbar {
        overflow: auto !important;
        scrollbar-gutter: auto !important;
        scrollbar-width: thin !important;
        scrollbar-color: rgba(228, 228, 231, 0.6) rgba(241, 245, 249, 0.5) !important;
      }

      .custom-scrollbar:hover {
        scrollbar-color: rgba(228, 228, 231, 1) rgba(241, 245, 249, 0.7) !important;
      }

      .dark .custom-scrollbar {
        scrollbar-color: rgba(113, 113, 122, 0.6) rgba(39, 39, 42, 0.5) !important;
      }

      .dark .custom-scrollbar:hover {
        scrollbar-color: rgba(113, 113, 122, 1) rgba(39, 39, 42, 0.7) !important;
      }

      /* WebKit overlay scrollbar - always visible */
      .custom-scrollbar::-webkit-scrollbar {
        width: 8px !important;
        height: 8px !important;
        background: transparent !important;
      }

      .custom-scrollbar::-webkit-scrollbar-track {
        background-color: rgba(241, 245, 249, 0.5) !important;
        margin: 0 !important;
        border: none !important;
        border-radius: 8px !important;
      }

      .dark .custom-scrollbar::-webkit-scrollbar-track {
        background-color: rgba(39, 39, 42, 0.5) !important;
      }

      .custom-scrollbar::-webkit-scrollbar-thumb {
        background-color: rgba(228, 228, 231, 0.6) !important;
        border-radius: 8px !important;
        border: 2px solid transparent !important;
        background-clip: padding-box !important;
        transition: background-color 0.2s !important;
      }

      .custom-scrollbar:hover::-webkit-scrollbar-thumb {
        background-color: rgba(228, 228, 231, 1) !important;
      }

      .custom-scrollbar::-webkit-scrollbar-thumb:hover {
        background-color: rgba(82, 82, 91, 1) !important;
      }

      .dark .custom-scrollbar::-webkit-scrollbar-thumb {
        background-color: rgba(113, 113, 122, 0.6) !important;
      }

      .dark .custom-scrollbar:hover::-webkit-scrollbar-thumb {
        background-color: rgba(113, 113, 122, 1) !important;
      }

      .dark .custom-scrollbar::-webkit-scrollbar-thumb:hover {
        background-color: rgba(161, 161, 170, 1) !important;
      }

      /* Make scrollbar overlay by extending table into scrollbar area */
      .custom-scrollbar {
        overflow-x: hidden !important;
      }

      .custom-scrollbar > table {
        width: calc(100% + 8px) !important;
        margin-right: -8px !important;
      }
    `
    document.head.appendChild(style)

    return () => {
      const existingStyle = document.getElementById(styleId)
      if (existingStyle) {
        existingStyle.remove()
      }
    }
  }, [])

  const providers = useMemo(() => {
    const uniqueProviders = new Set()
    models.forEach(model => {
      if (model.provider) {
        uniqueProviders.add(model.provider)
      }
    })
    return Array.from(uniqueProviders).sort()
  }, [models])

  const filteredModels = useMemo(() => {
    let filtered = models

    // Filter by provider
    if (selectedProvider !== 'all') {
      filtered = filtered.filter(model => model.provider === selectedProvider)
    }

    // Filter by search term
    if (searchTerm) {
      const term = searchTerm.toLowerCase()
      filtered = filtered.filter(model =>
        Object.values(model).some(value =>
          String(value).toLowerCase().includes(term)
        )
      )
    }

    return filtered
  }, [models, searchTerm, selectedProvider])

  const formatColumnName = useCallback((name) => {
    // Handle snake_case: replace underscores with spaces
    let formatted = name.replace(/_/g, ' ')
    // Handle camelCase: add space before capital letters
    formatted = formatted.replace(/([A-Z])/g, ' $1')
    // Capitalize first letter of each word and trim
    return formatted
      .split(' ')
      .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
      .join(' ')
      .trim()
  }, [])

  const handleRowClick = useCallback((model) => {
    setSelectedModel(model)
    setShowDialog(true)
  }, [])

  if (loading) {
    return (
      <div className="p-10 text-center border border-zinc-950/20 dark:border-white/20 rounded-lg">
        <div className="text-zinc-950/70 dark:text-white/70">Loading models...</div>
      </div>
    )
  }

  if (error) {
    return (
      <div className="p-10 text-center border border-red-200 dark:border-red-800 rounded-lg bg-red-50 dark:bg-red-950">
        <div className="text-red-600 dark:text-red-400">Error: {error}</div>
        <div className="text-sm text-red-500 dark:text-red-500 mt-2">Check console for details</div>
      </div>
    )
  }

  if (!models || models.length === 0) {
    return (
      <div className="p-10 text-center border border-zinc-950/20 dark:border-white/20 rounded-lg">
        <div className="text-zinc-950/70 dark:text-white/70">No models available</div>
      </div>
    )
  }


  return (
    <div className="w-full not-prose" style={{ display: 'flex', flexDirection: 'column' }}>
      <div className="sticky top-0 z-20 mb-4 pb-4 bg-white dark:bg-zinc-950" style={{ paddingTop: '1rem' }}>
        <div className="flex gap-3 mb-3">
          <input
            type="text"
            placeholder="Search models, providers, or any field..."
            value={searchTerm}
            onChange={(e) => setSearchTerm(e.target.value)}
            className="flex-1 px-3 py-2 text-base border-2 border-zinc-950/20 dark:border-white/20 rounded-lg focus:outline-none focus:border-[#0C3B43] dark:focus:border-[#07C983] transition-colors bg-white dark:bg-zinc-950 text-zinc-950 dark:text-white"
          />
          <select
            value={selectedProvider}
            onChange={(e) => setSelectedProvider(e.target.value)}
            className="px-3 py-2 text-base border-2 border-zinc-950/20 dark:border-white/20 rounded-lg focus:outline-none focus:border-[#0C3B43] dark:focus:border-[#07C983] transition-colors bg-white dark:bg-zinc-950 text-zinc-950 dark:text-white"
          >
            <option value="all">All Providers</option>
            {providers.map(provider => (
              <option key={provider} value={provider}>
                {provider.charAt(0).toUpperCase() + provider.slice(1)}
              </option>
            ))}
          </select>
        </div>
        <div className="text-sm text-zinc-950/70 dark:text-white/70">
          Showing {filteredModels.length} of {models.length} models
        </div>
      </div>

      <div className="overflow-y-auto custom-scrollbar border border-zinc-950/20 dark:border-white/20 rounded-lg" style={{ maxHeight: '600px', padding: 0, margin: 0, overflowX: 'hidden' }}>
        <table className="w-full text-sm bg-white dark:bg-zinc-950" style={{ borderCollapse: 'collapse', tableLayout: 'fixed', margin: 0, padding: 0 }}>
          <thead className="sticky bg-zinc-50 dark:bg-zinc-900" style={{ top: 0, zIndex: 10 }}>
            <tr>
              <th className="px-6 py-3 text-left font-semibold text-zinc-950 dark:text-white whitespace-nowrap border-b-2 border-zinc-950/20 dark:border-white/20" style={{ width: '200px' }}>
                Provider
              </th>
              <th className="px-6 py-3 text-left font-semibold text-zinc-950 dark:text-white whitespace-nowrap border-b-2 border-zinc-950/20 dark:border-white/20">
                Model
              </th>
              <th className="px-6 py-3 text-center font-semibold text-zinc-950 dark:text-white whitespace-nowrap border-b-2 border-zinc-950/20 dark:border-white/20" style={{ width: '150px' }}>
                Details
              </th>
            </tr>
          </thead>
          <tbody>
            {filteredModels.length === 0 && searchTerm ? (
              <tr>
                <td colSpan={3} className="px-6 py-10 text-center text-zinc-950/70 dark:text-white/70">
                  No models found matching "{searchTerm}"
                </td>
              </tr>
            ) : (
              filteredModels.map((model, idx) => (
                <tr
                  key={`${model.provider}-${model.model}`}
                  className="transition-colors hover:bg-zinc-50 dark:hover:bg-zinc-900"
                >
                  <td className="px-6 py-3 text-zinc-950/80 dark:text-white/80 border-b border-zinc-950/10 dark:border-white/10 capitalize">
                    {model.provider || '—'}
                  </td>
                  <td className="px-6 py-3 text-zinc-950/80 dark:text-white/80 border-b border-zinc-950/10 dark:border-white/10 font-mono">
                    {model.model || '—'}
                  </td>
                  <td className="px-6 py-3 border-b border-zinc-950/10 dark:border-white/10 text-center">
                    <button
                      onClick={() => handleRowClick(model)}
                      className="text-[#0C3B43] dark:text-[#07C983] hover:underline font-medium text-sm"
                    >
                      View Details
                    </button>
                  </td>
                </tr>
              ))
            )}
          </tbody>
        </table>
      </div>

      {showDialog && selectedModel && <ModelDialog model={selectedModel} onClose={() => setShowDialog(false)} />}
    </div>
  )
}


<ModelsCatalog />

