HTML 77.2%
TypeScript 10.5%
Python 9.6%
JavaScript 2.5%
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