oxios 1.10.1

Oxios Agent OS — Agent Operating System powered by oxi-sdk
import { useNavigate } from '@tanstack/react-router'
import { Check, ChevronsUpDown, Key, Loader2, Route, Settings } from 'lucide-react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Separator } from '@/components/ui/separator'
import { useEngineConfig, useModels, useProviders, useSetModel } from '@/hooks/use-engine'
import type { ModelInfo, ProviderInfo } from '@/types/engine'

/**
 * Interactive providers section for SystemHealthCard.
 *
 * - Shows configured providers as badges.
 * - Click a provider → expands its model list.
 * - Click a model → switches immediately via PUT /api/engine/model.
 * - Shows routing configuration below.
 */
export function ProvidersSection() {
  const { t } = useTranslation()
  const navigate = useNavigate()
  const { data: engineConfig } = useEngineConfig()
  const { data: providers } = useProviders()
  const setModel = useSetModel()

  const currentModel = engineConfig?.default_model ?? ''
  const routing = engineConfig?.routing
  const routingEnabled = routing?.routingEnabled ?? false
  const currentProviderId = currentModel.includes('/') ? currentModel.split('/')[0] : null
  const currentModelId = currentModel.includes('/')
    ? currentModel.split('/').slice(1).join('/')
    : currentModel

  const configuredProviders = (providers ?? []).filter((p) => p.hasKey)

  const [expandedProvider, setExpandedProvider] = useState<string | null>(null)

  return (
    <div className="space-y-2">
      {/* Header */}
      <div className="flex items-center justify-between">
        <h3 className="text-xs font-semibold text-muted-foreground uppercase tracking-wide">
          {t('dashboard.providers')}
        </h3>
        <button
          type="button"
          onClick={() => navigate({ to: '/settings', search: { section: 'engine' } })}
          className="text-muted-foreground hover:text-foreground transition-colors"
          title={t('dashboard.engineSettings')}
        >
          <Settings className="h-3.5 w-3.5" />
        </button>
      </div>

      {/* Current model */}
      <div className="flex items-center gap-2 text-xs">
        <span className="text-muted-foreground">{t('dashboard.currentModel')}:</span>
        <span className="font-semibold truncate">
          {currentModelId || t('dashboard.modelNotSet')}
        </span>
        {currentProviderId && (
          <span className="inline-flex items-center rounded-full bg-primary/10 px-1.5 py-0.5 text-2xs font-medium text-primary whitespace-nowrap">
            {providers?.find((p) => p.id === currentProviderId)?.name ?? currentProviderId}
          </span>
        )}
      </div>

      {/* Configured providers */}
      {configuredProviders.length > 0 && (
        <div className="space-y-1.5">
          <div className="flex flex-wrap gap-1.5">
            {configuredProviders.map((p) => (
              <ProviderBadge
                key={p.id}
                provider={p}
                isCurrent={p.id === currentProviderId}
                expanded={expandedProvider === p.id}
                switching={setModel.isPending}
                onToggle={() => setExpandedProvider((prev) => (prev === p.id ? null : p.id))}
              />
            ))}
          </div>

          {/* Model picker for expanded provider */}
          {expandedProvider && (
            <ModelPicker
              providerId={expandedProvider}
              currentModel={currentModel}
              onSelect={(fullModelId) => setModel.mutate(fullModelId)}
              isPending={setModel.isPending}
            />
          )}
        </div>
      )}

      {/* Routing status */}
      {routing && (
        <>
          <Separator />
          <div className="space-y-1">
            <div className="flex items-center gap-2 text-xs">
              <Route
                className={`h-3.5 w-3.5 ${routingEnabled ? 'text-success' : 'text-muted-foreground'}`}
              />
              <span className="text-muted-foreground">{t('dashboard.routing')}:</span>
              <span
                className={routingEnabled ? 'text-success font-medium' : 'text-muted-foreground'}
              >
                {routingEnabled ? t('dashboard.routingEnabled') : t('dashboard.routingDisabled')}
              </span>
            </div>
            {routingEnabled && (
              <div className="pl-5 space-y-0.5">
                {routing.preferCostEfficient && (
                  <p className="text-2xs text-muted-foreground">{t('dashboard.costOptimized')}</p>
                )}
                {routing.fallbackModels.length > 0 && (
                  <p className="text-2xs text-muted-foreground">
                    {t('dashboard.fallbackModels', { count: routing.fallbackModels.length })}
                    {': '}
                    <span className="font-mono">
                      {routing.fallbackModels
                        .slice(0, 3)
                        .map((m) => m.split('/').pop())
                        .join(', ')}
                      {routing.fallbackModels.length > 3 &&
                        ` +${routing.fallbackModels.length - 3}`}
                    </span>
                  </p>
                )}
                {routing.excludedModels.length > 0 && (
                  <p className="text-2xs text-muted-foreground">
                    {t('dashboard.excludedModels', { count: routing.excludedModels.length })}
                  </p>
                )}
              </div>
            )}
          </div>
        </>
      )}
    </div>
  )
}

// ─── Provider Badge ───────────────────────────────────────────

function ProviderBadge({
  provider,
  isCurrent,
  expanded,
  switching,
  onToggle,
}: {
  provider: ProviderInfo
  isCurrent: boolean
  expanded: boolean
  switching: boolean
  onToggle: () => void
}) {
  return (
    <button
      type="button"
      onClick={onToggle}
      disabled={switching}
      className={`inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-2xs font-medium whitespace-nowrap transition-colors ${
        expanded
          ? 'bg-primary/15 text-primary ring-1 ring-primary/30'
          : isCurrent
            ? 'bg-primary/10 text-primary'
            : 'bg-muted text-muted-foreground hover:bg-accent'
      }`}
      title={`${provider.name}${provider.hasKey ? ' (API key configured)' : ''}`}
    >
      <Key
        className={`h-2.5 w-2.5 ${isCurrent || expanded ? 'text-primary' : 'text-muted-foreground/60'}`}
      />
      {provider.name}
      {expanded ? (
        <ChevronsUpDown className="h-2.5 w-2.5 ml-0.5" />
      ) : provider.modelCount > 0 ? (
        <span className="text-muted-foreground/60">({provider.modelCount})</span>
      ) : null}
    </button>
  )
}

// ─── Model Picker ─────────────────────────────────────────────

function ModelPicker({
  providerId,
  currentModel,
  onSelect,
  isPending,
}: {
  providerId: string
  currentModel: string
  onSelect: (fullModelId: string) => void
  isPending: boolean
}) {
  const { t } = useTranslation()
  const { data: models, isLoading } = useModels(providerId)

  if (isLoading) {
    return (
      <div className="flex items-center gap-1.5 px-2 py-1.5 text-2xs text-muted-foreground">
        <Loader2 className="h-3 w-3 animate-spin" />
        {t('common.loading')}
      </div>
    )
  }

  const list = Array.isArray(models) ? models : []
  if (list.length === 0) {
    return <p className="px-2 text-2xs text-muted-foreground">{t('dashboard.noModels')}</p>
  }

  return (
    <div className="max-h-[160px] overflow-y-auto rounded-md border bg-background p-1 space-y-0.5">
      {list.map((m: ModelInfo) => {
        const fullId = `${providerId}/${m.id}`
        const isActive = fullId === currentModel
        return (
          <button
            key={m.id}
            type="button"
            disabled={isPending}
            onClick={() => onSelect(fullId)}
            className={`flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-xs transition-colors ${
              isActive
                ? 'bg-primary/10 text-primary font-medium'
                : 'hover:bg-accent text-foreground'
            } ${isPending ? 'opacity-50' : ''}`}
          >
            <span className={`shrink-0 ${isActive ? 'text-primary' : 'text-transparent'}`}>
              <Check className="h-3 w-3" />
            </span>
            <span className="flex-1 truncate">{m.name}</span>
            {m.reasoning && (
              <span className="shrink-0 rounded-full bg-info/10 px-1.5 py-0 text-2xs text-info font-medium">
                reasoning
              </span>
            )}
            {(m.costInput > 0 || m.costOutput > 0) && (
              <span className="shrink-0 text-2xs text-muted-foreground tabular-nums">
                ${m.costInput}/${m.costOutput}
              </span>
            )}
          </button>
        )
      })}
    </div>
  )
}