"use client"; import * as React from "react"; import { Brain, Eye, FileText, Globe, Wrench, Braces, Zap, Maximize2 } from "lucide-react"; import type { PolyModel } from "@/lib/client/types"; import { Badge } from "@/components/ui/badge"; import { Tooltip } from "@/components/ui/tooltip"; import { useApp } from "@/components/app/store"; import { BadgeChips, LifecycleChip } from "@/components/models/badge-chips"; import { buildBadgeContext, deriveBadges, isFastModel as isFastModelBase, lifecycleStatus, retiresSoon as retiresSoonBase, shutdownDateOf, LONG_CONTEXT_TOKENS, type BadgeContext } from "@/lib/models/badges"; import { formatPrice } from "@/lib/models/format"; import { formatTokens } from "@/lib/utils"; import { cn } from "@/lib/utils"; /** Capability glyph definitions shared by rows, profile and catalog (order = display order). */ export const CAPABILITY_GLYPHS: { key: keyof PolyModel["capabilities"]; label: string; icon: React.ReactNode; title: string }[] = [ { key: "vision", label: "Vision", icon: , title: "Understands images" }, { key: "reasoning", label: "Reasoning", icon: , title: "Extended reasoning / thinking" }, { key: "tools", label: "Tools", icon: , title: "Function / tool calling" }, { key: "webSearch", label: "Web", icon: , title: "Provider-native web search" }, { key: "structuredOutput", label: "JSON", icon: , title: "Structured output (JSON schema)" }, { key: "files", label: "PDF", icon: , title: "PDF / file input" }, ]; export function capabilityList(m: PolyModel) { const out: { key: string; label: string; icon: React.ReactNode; title: string }[] = CAPABILITY_GLYPHS.filter((g) => m.capabilities[g.key]).map((g) => ({ key: g.key, label: g.label, icon: g.icon, title: g.title })); if ((m.limits?.contextTokens ?? 0) >= LONG_CONTEXT_TOKENS) out.push({ key: "long", label: formatTokens(m.limits!.contextTokens!), icon: , title: "Long context window" }); return out; } /** Kept for existing callers (arena auto-pick). Prefer `speedTier()` from `lib/models/badges` for new code. */ export function isFastModel(m: PolyModel): boolean { return isFastModelBase(m); } export const retiresSoon = retiresSoonBase; /** Registry-wide badge context (pricing quantiles, "new" window) memoized on the store's model list. */ export function useBadgeContext(): BadgeContext { const { models } = useApp(); const [now] = React.useState(() => Date.now()); return React.useMemo(() => buildBadgeContext(models, now), [models, now]); } /** Compact icon-only capability row: Vision · Reasoning · Tools · Web · JSON · PDF. */ export function CapabilityGlyphs({ model, max = 6, className, size = "sm" }: { model: PolyModel; max?: number; className?: string; size?: "sm" | "md" }) { const on = CAPABILITY_GLYPHS.filter((g) => model.capabilities[g.key]).slice(0, max); if (!on.length) return text only; return ( g.label).join(", ")}> {on.map((g) => ( {g.icon} ))} ); } export function CapabilityBadges({ model, compact, max = 6, className }: { model: PolyModel; compact?: boolean; max?: number; className?: string }) { const caps = capabilityList(model).slice(0, max); return (
{isFastModel(model) ? ( {compact ? null : "Fast"} ) : null} {caps.map((c) => ( {c.icon} {compact ? null : c.label} ))}
); } /** NEW / FAST / CHEAP / REASONING / VISION / CODING / LONG CONTEXT from real registry data. */ export function ModelBadges({ model, ctx, max = 3, className, size }: { model: PolyModel; ctx: BadgeContext; max?: number; className?: string; size?: "xs" | "sm" }) { const badges = React.useMemo(() => deriveBadges(model, ctx), [model, ctx]); return ; } export function PriceLabel({ model, className }: { model: PolyModel; className?: string }) { const p = model.pricing; if (!p || (p.inputPerMillion === undefined && p.outputPerMillion === undefined)) return price n/a; return ( {formatPrice(p.inputPerMillion)} / {formatPrice(p.outputPerMillion)} per 1M ); } /** * Lifecycle badge: New / Active / Preview / Deprecated / Retiring / Unavailable. * `showActive` renders the green "Active" chip too (catalog); otherwise Active/New-less models render nothing. */ export function StatusBadge({ model, connected, ctx, showActive = false, className }: { model: PolyModel; connected?: boolean; ctx?: BadgeContext; showActive?: boolean; className?: string }) { const [now] = React.useState(() => Date.now()); const lc = lifecycleStatus(model, { connected, ctx, now }); if (lc.status === "active" && !showActive) return null; const sd = shutdownDateOf(model); return ; }