SPB Git forge

spb/ai-atlas

Public
41commits 1branches 0releases
4.6 MBsize
maindefault branch
12 days agolast push
HTML 77.2% TypeScript 10.5% Python 9.6% JavaScript 2.5%
7.0 KB · 173 lines tsx
Raw Blame History
1import Link from 'next/link';2import { APPLE_PRESETS, NVIDIA_PRESETS, QUANTS } from '@/components/hardware/fit-form';3import { BTN_GHOST, CTRL, Field } from './bits';45/*6  GET form for /run-locally (no JavaScript needed). Memory presets by platform + a free field; GPU count; quantization; context;7  batch; use-case filter. In "hardware mode" (`?hardware=<slug>`) the memory select lists the device's own memory options.8*/9export const AMD_PRESETS = [16, 24, 32, 48, 128, 192, 256];10export const GPU_COUNTS = [1, 2, 4, 8];11export const PLATFORMS = [12  { value: 'any', label: 'Any platform' },13  { value: 'apple', label: 'Apple silicon (MLX, llama.cpp)' },14  { value: 'nvidia', label: 'NVIDIA (CUDA, vLLM, TensorRT-LLM)' },15  { value: 'amd', label: 'AMD (ROCm)' },16];17export const CONTEXT_PRESETS = [18  { value: 4096, label: '4K' },19  { value: 8192, label: '8K' },20  { value: 32768, label: '32K' },21  { value: 131072, label: '128K' },22  { value: 262144, label: '256K' },23  { value: 1000000, label: '1M' },24];25export const BATCHES = [1, 2, 4, 8, 16];26export const USE_CASES = [27  { value: '', label: 'Any use case' },28  { value: 'chat', label: 'Chat' },29  { value: 'coding', label: 'Coding' },30  { value: 'reasoning', label: 'Reasoning' },31  { value: 'agentic', label: 'Agentic / tool use' },32  { value: 'vision', label: 'Vision' },33  { value: 'embeddings', label: 'Embeddings' },34  { value: 'long_context', label: 'Long context' },35];3637export type RunLocallyInputs = { memory: number | null; gpuCount: number; quant: string; context: number; batch: number; platform: string; useCase: string; openness: string; hardware: string | null; fitsOnly: boolean };3839export function RunLocallyForm({ v, hardwareName, memoryOptions, className }: { v: RunLocallyInputs; hardwareName?: string | null; memoryOptions?: number[]; className?: string }) {40  const presets = [...APPLE_PRESETS, ...NVIDIA_PRESETS, ...AMD_PRESETS];41  const presetSelected = v.memory !== null && (memoryOptions?.includes(v.memory) || presets.includes(v.memory)) ? String(v.memory) : '';42  return (43    <form action="/run-locally" method="get" className={`space-y-3 ${className ?? ''}`} data-run-locally-form>44      {v.hardware && (45        <div className="border border-rule bg-surface-2 px-2.5 py-2 text-xs text-ink-2">46          <input type="hidden" name="hardware" value={v.hardware} />47          Device: <span className="font-medium text-ink">{hardwareName ?? v.hardware}</span>{' '}48          <Link href="/run-locally" className="link">49            change50          </Link>51        </div>52      )}53      {!v.hardware && (54        <Field label="Platform">55          <select name="platform" defaultValue={v.platform} className={CTRL}>56            {PLATFORMS.map((p) => (57              <option key={p.value} value={p.value}>58                {p.label}59              </option>60            ))}61          </select>62        </Field>63      )}64      <Field label={v.hardware ? 'Device memory' : 'Memory per device (preset)'}>65        <select name="memory_gb" defaultValue={presetSelected} className={CTRL}>66          <option value="">{v.hardware ? 'Largest configuration' : 'Choose a preset…'}</option>67          {memoryOptions && memoryOptions.length > 0 ? (68            memoryOptions.map((g) => (69              <option key={g} value={g}>70                {g} GB71              </option>72            ))73          ) : (74            <>75              <optgroup label="Apple silicon (unified memory)">76                {APPLE_PRESETS.map((g) => (77                  <option key={`a${g}`} value={g}>78                    {g} GB79                  </option>80                ))}81              </optgroup>82              <optgroup label="NVIDIA (VRAM)">83                {NVIDIA_PRESETS.map((g) => (84                  <option key={`n${g}`} value={g}>85                    {g} GB86                  </option>87                ))}88              </optgroup>89              <optgroup label="AMD (VRAM)">90                {AMD_PRESETS.map((g) => (91                  <option key={`d${g}`} value={g}>92                    {g} GB93                  </option>94                ))}95              </optgroup>96            </>97          )}98        </select>99      </Field>100      {!v.hardware && (101        <Field label="…or any memory (GB)" hint="Wins over the preset when filled.">102          <input name="memory_custom" inputMode="decimal" pattern="[0-9]*\.?[0-9]*" defaultValue={presetSelected ? '' : v.memory ?? ''} placeholder="e.g. 20" className={CTRL} />103        </Field>104      )}105      <Field label="GPUs / devices" hint="Memories are summed; interconnect is not modelled.">106        <select name="gpu_count" defaultValue={String(v.gpuCount)} className={CTRL}>107          {GPU_COUNTS.map((n) => (108            <option key={n} value={n}>109              {n}110            </option>111          ))}112        </select>113      </Field>114      <Field label="Quantization">115        <select name="quant" defaultValue={v.quant} className={CTRL}>116          {QUANTS.map((q) => (117            <option key={q.value} value={q.value}>118              {q.label}119            </option>120          ))}121        </select>122      </Field>123      <Field label="Context window">124        <select name="context" defaultValue={String(v.context)} className={CTRL}>125          {CONTEXT_PRESETS.map((c) => (126            <option key={c.value} value={c.value}>127              {c.label} tokens128            </option>129          ))}130          {!CONTEXT_PRESETS.some((c) => c.value === v.context) && <option value={v.context}>{v.context} tokens</option>}131        </select>132      </Field>133      <Field label="Batch (concurrent sequences)">134        <select name="batch" defaultValue={String(v.batch)} className={CTRL}>135          {BATCHES.map((b) => (136            <option key={b} value={b}>137              {b}138            </option>139          ))}140        </select>141      </Field>142      <Field label="Use case">143        <select name="use_case" defaultValue={v.useCase} className={CTRL}>144          {USE_CASES.map((u) => (145            <option key={u.value} value={u.value}>146              {u.label}147            </option>148          ))}149        </select>150      </Field>151      <Field label="Openness">152        <select name="openness" defaultValue={v.openness} className={CTRL}>153          <option value="">Any downloadable weights</option>154          <option value="open-source">Open source</option>155          <option value="open-weights">Open weights</option>156          <option value="restricted-weights">Restricted weights</option>157        </select>158      </Field>159      <label className="flex min-h-11 items-center gap-2 text-sm text-ink-2">160        <input type="checkbox" name="fits" value="1" defaultChecked={v.fitsOnly} className="size-4 accent-[var(--accent)]" /> Only models that fit161      </label>162      <div className="flex gap-2">163        <button type="submit" className="inline-flex h-11 flex-1 items-center justify-center bg-ink lg:h-10 px-3 text-sm font-medium text-canvas hover:opacity-90">164          Estimate165        </button>166        <Link href="/run-locally" className={BTN_GHOST}>167          Reset168        </Link>169      </div>170    </form>171  );172}173