Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions app/gpu-explorer/GpuBubbleChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,18 @@ export function GpuBubbleChart({ data, width, height, xLabel, yLabel }: Props) {
const sizeValues = data.map(d => d.size);

const xMin = 0;
const xMax = Math.max(...xValues) * 1.1;
const xMax = Math.max(...xValues) * 1.1 || 1;
const yMin = 0;
const yMax = Math.max(...yValues) * 1.1;
const yMax = Math.max(...yValues) * 1.1 || 1;
const sizeMin = Math.min(...sizeValues);
const sizeMax = Math.max(...sizeValues);
const sizeRange = sizeMax - sizeMin || 1;
Comment thread
mazam-lab marked this conversation as resolved.
Outdated

// Scale functions
const scaleX = (val: number) => (val / xMax) * chartWidth;
const scaleY = (val: number) => chartHeight - (val / yMax) * chartHeight;
const scaleSize = (val: number) => {
// Map size values to radius 5-30px
const normalized = (val - sizeMin) / (sizeMax - sizeMin);
const normalized = (val - sizeMin) / sizeRange;
return 5 + normalized * 25;
};

Expand Down
24 changes: 13 additions & 11 deletions app/kv-cache/KvCacheCalc.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import { useAicCatalog } from '@/lib/hooks/useAicCatalog'
import { useSettings, type InferenceBackend } from '@/contexts/SettingsContext'
import { getAppConfig } from '@/lib/app-config'
import { ModelInput, type ModelStatus } from '@/components/ui/ModelInput'
import { ComboBox, type ComboBoxItem } from '@/components/ModelComboBox/ModelComboBox'

Check failure on line 11 in app/kv-cache/KvCacheCalc.tsx

View workflow job for this annotation

GitHub Actions / Type-check, lint, and build

Cannot find module '@/components/ModelComboBox/ModelComboBox' or its corresponding type declarations.
Comment thread
mazam-lab marked this conversation as resolved.
import { GpuSystemInput } from '@/components/ui/GpuSystemInput'
import type { KvCacheCalcResult } from '@/lib/api/kv-cache-calc'
import styles from './KvCacheCalc.module.css'
Expand All @@ -27,6 +27,12 @@
const { modelOptions: aicModels, gpuOptions: aicGpus, isLoading: catalogLoading } = useAicCatalog()
const MODEL_OPTIONS = aicModels

const modelItems: ComboBoxItem[] = React.useMemo(() =>
aicModels.map(m => {
const slash = m.indexOf('/');
return { value: m, label: m, group: slash > 0 ? m.slice(0, slash) : '' };
}), [aicModels]);

const [model, setModel] = React.useState('')
const [system, setSystem] = React.useState(() => getAppConfig().defaultSystem)
const [backend, setBackend] = React.useState(() => getAppConfig().defaultBackend)
Expand Down Expand Up @@ -111,11 +117,6 @@
};

const catalogMatch = MODEL_OPTIONS.includes(model)
const kvModelStatus: ModelStatus = getAppConfig().supportedModels.includes(model)
? 'supported'
: catalogMatch ? 'catalog'
: catalogLoading ? 'fetching'
: model ? 'idle' : 'idle'

async function handleCalculate() {
setLoading(true)
Expand Down Expand Up @@ -194,13 +195,14 @@
<div className={styles.inputCard}>
<div className={styles.inputRow}>
<div className={styles.field}>
<ModelInput
<label className={styles.fieldLabel} htmlFor="kv-model">Model — Hugging Face ID</label>
<ComboBox
id="kv-model"
model={model}
value={model}
onChange={setModel}
modelOptions={aicModels}
isLoading={catalogLoading}
status={kvModelStatus}
items={modelItems}
placeholder="Type model name or select from dropdown..."
allowCustom
/>
</div>

Expand Down
32 changes: 13 additions & 19 deletions app/performance/PerformanceEstimate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import { saveEstimate, getSavedEstimateCount } from '@/lib/saved-estimates';
import { fetchEstimateAsInferenceResult, EstimateError } from '@/lib/api/estimate-adapter';
import { InfoStrip, InfoStripAction } from '@/components/ui/InfoStrip';
import { ModelInput, type ModelStatus } from '@/components/ui/ModelInput';
import { ComboBox, type ComboBoxItem } from '@/components/ModelComboBox/ModelComboBox';

Check failure on line 31 in app/performance/PerformanceEstimate.tsx

View workflow job for this annotation

GitHub Actions / Type-check, lint, and build

Cannot find module '@/components/ModelComboBox/ModelComboBox' or its corresponding type declarations.
import { GpuSystemInput } from '@/components/ui/GpuSystemInput';
import { useAicCatalog } from '@/lib/hooks/useAicCatalog';
import { GpuChipLoader } from '@/components/GpuChipLoader/GpuChipLoader';
Expand Down Expand Up @@ -78,6 +78,12 @@
const { hydrated, hfToken, defaultModel: settingsDefaultModel, inferenceBackend, backendVersion } = useSettings();
const { gpuOptions: aicGpus, modelOptions: aicModels, modelSpecs, isLoading: catalogLoading } = useAicCatalog();

const modelItems: ComboBoxItem[] = React.useMemo(() =>
aicModels.map(m => {
const slash = m.indexOf('/');
return { value: m, label: m, group: slash > 0 ? m.slice(0, slash) : '' };
}), [aicModels]);

const [model, setModel] = React.useState('');
const [gpu, setGpu] = React.useState(() => getAppConfig().defaultSystem);

Expand Down Expand Up @@ -113,18 +119,6 @@
const [isUsingFallback, setIsUsingFallback] = React.useState(false);
const [fallbackReason, setFallbackReason] = React.useState<string>('');

const modelStatus: ModelStatus = getAppConfig().supportedModels.includes(model)
? 'supported'
: aicModels.includes(model)
? 'catalog'
: isFetchingConfig || catalogLoading
? 'fetching'
: hfConfig
? 'fetched'
: testError
? 'error'
: 'idle';

// Collapsible state for "Why this GPU count?" card
const [whyGpuExpanded, setWhyGpuExpanded] = React.useState(false);

Expand Down Expand Up @@ -916,14 +910,14 @@
<div className={styles.inputRow}>
{/* Column 1: Model field */}
<div>
<ModelInput
<label className={styles.fieldLabel} htmlFor="qe-model">Model — Hugging Face ID</label>
<ComboBox
id="qe-model"
model={model}
value={model}
onChange={setModel}
modelOptions={aicModels}
isLoading={catalogLoading}
hfToken={hfToken}
status={modelStatus}
items={modelItems}
placeholder="Type model name or select from dropdown..."
allowCustom
/>
</div>

Expand Down
39 changes: 12 additions & 27 deletions app/recommend/AdvancedEstimate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@
import { InfoStrip, InfoStripAction } from '@/components/ui/InfoStrip';

import styles from './AdvancedEstimate.module.css';
import { fetchModelConfig } from '@/lib/huggingface/fetch-config';
import { useGpuSizer } from '@/contexts/GpuSizerContext';
import { useAicCatalog } from '@/lib/hooks/useAicCatalog';
import { useSettings } from '@/contexts/SettingsContext';
import { getAppConfig } from '@/lib/app-config';
import { ModelInput } from '@/components/ui/ModelInput';
import { ComboBox, type ComboBoxItem } from '@/components/ModelComboBox/ModelComboBox';

Check failure on line 20 in app/recommend/AdvancedEstimate.tsx

View workflow job for this annotation

GitHub Actions / Type-check, lint, and build

Cannot find module '@/components/ModelComboBox/ModelComboBox' or its corresponding type declarations.
import { GpuSystemInput } from '@/components/ui/GpuSystemInput';

function modelSuggestions(): string {
Expand Down Expand Up @@ -136,6 +135,12 @@
const { modelOptions: aicModels, gpuOptions: aicGpus, isLoading: catalogLoading } = useAicCatalog();
const MODEL_OPTIONS = aicModels;

const modelItems: ComboBoxItem[] = React.useMemo(() =>
aicModels.map(m => {
const slash = m.indexOf('/');
return { value: m, label: m, group: slash > 0 ? m.slice(0, slash) : '' };
}), [aicModels]);

// Input state
const [model, setModel] = React.useState('');

Expand All @@ -151,9 +156,6 @@
const [osl, setOsl] = React.useState(128);
const [ttft, setTtft] = React.useState(1000);

// Model status + HF config
const [modelStatus, setModelStatus] = React.useState<'idle' | 'supported' | 'catalog' | 'fetching' | 'fetched' | 'error'>('idle');

// GPU sizer (persistent across navigation)
const { isLoading, result, error, errorCode, elapsed, debugRequest, debugResponse, debugStatus, debugDuration, startSizing } = useGpuSizer();
const [debugOpen, setDebugOpen] = React.useState(false);
Expand Down Expand Up @@ -194,22 +196,6 @@
};


// Model status check + fetch HF config
React.useEffect(() => {
if (catalogLoading) { setModelStatus('idle'); return; }
const timer = setTimeout(() => {
if (!model.includes('/')) { setModelStatus('idle'); return; }
if (getAppConfig().supportedModels.includes(model)) { setModelStatus('supported'); return; }
const inCatalog = MODEL_OPTIONS.includes(model);
if (inCatalog) { setModelStatus('catalog'); return; }
setModelStatus('fetching');
fetchModelConfig(model, hfToken).then(r => {
setModelStatus(r.success && r.config ? 'fetched' : 'error');
});
}, 500);
return () => clearTimeout(timer);
}, [model, hfToken, MODEL_OPTIONS, catalogLoading, hydrated]);

// Fetch live pricing
React.useEffect(() => {
const fetchPricing = async () => {
Expand Down Expand Up @@ -267,15 +253,14 @@
{/* Model + GPU row */}
<div className={styles.inputGrid}>
<div>
<ModelInput
<label className={styles.fieldLabel} htmlFor="adv-model">Model — Hugging Face ID</label>
<ComboBox
id="adv-model"
model={model}
value={model}
onChange={setModel}
modelOptions={aicModels}
isLoading={catalogLoading}
hfToken={hfToken}
status={modelStatus}
items={modelItems}
placeholder="e.g. meta-llama/Llama-3.1-70B-Instruct"
allowCustom
/>
</div>

Expand Down
41 changes: 0 additions & 41 deletions components/ProductTour/ProductTour.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,33 +70,6 @@ export function ProductTour({ steps, tourId, onComplete }: ProductTourProps) {
height: spotlightHeight
});

// Sanity check: log all measurements
const targetCenterX = rect.left + rect.width / 2;
const targetCenterY = rect.top + rect.height / 2;
const spotlightCenterX = spotlightLeft + spotlightWidth / 2;
const spotlightCenterY = spotlightTop + spotlightHeight / 2;

console.log(`\n=== Step ${currentStep + 1}: "${step.title}" ===`);
console.log(`Target selector: ${step.target}`);
console.log(`Element found:`, targetEl);
console.log(`Target rect:`, {
top: rect.top.toFixed(1),
left: rect.left.toFixed(1),
width: rect.width.toFixed(1),
height: rect.height.toFixed(1),
bottom: rect.bottom.toFixed(1),
right: rect.right.toFixed(1)
});
console.log(`Spotlight rect:`, {
top: spotlightTop.toFixed(1),
left: spotlightLeft.toFixed(1),
width: spotlightWidth.toFixed(1),
height: spotlightHeight.toFixed(1)
});
console.log(`Target center: (${targetCenterX.toFixed(1)}, ${targetCenterY.toFixed(1)})`);
console.log(`Spotlight center: (${spotlightCenterX.toFixed(1)}, ${spotlightCenterY.toFixed(1)})`);
console.log(`Offset: ${(spotlightCenterY - targetCenterY).toFixed(1)}px vertical, ${(spotlightCenterX - targetCenterX).toFixed(1)}px horizontal`);

// Position tooltip based on step.position (FIXED positioning, no scroll offset)
let top = 0;
let left = 0;
Expand Down Expand Up @@ -185,20 +158,6 @@ export function ProductTour({ steps, tourId, onComplete }: ProductTourProps) {
{/* Invisible click target to skip tour */}
<div className={styles.overlay} onClick={handleSkip} />

{/* DEBUG: Red outline showing exact target position */}
<div
style={{
position: 'fixed',
top: `${spotlightRect.top + 8}px`,
left: `${spotlightRect.left + 8}px`,
width: `${spotlightRect.width - 16}px`,
height: `${spotlightRect.height - 16}px`,
border: '2px solid red',
pointerEvents: 'none',
zIndex: 10001,
}}
/>

{/* Spotlight on target element */}
<div
className={styles.spotlight}
Expand Down
23 changes: 19 additions & 4 deletions components/ui/GpuSystemInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@ interface GpuSystemInputProps {
export function GpuSystemInput({ id, value, onChange, gpuOptions }: GpuSystemInputProps) {
const current = gpuOptions.find(g => g.systemId === value)

const vendorGroups = React.useMemo(() => {
const groups = new Map<string, GpuOption[]>()
for (const g of gpuOptions) {
const key = g.vendor ?? 'other'
const list = groups.get(key)
if (list) list.push(g)
else groups.set(key, [g])
}
return groups
}, [gpuOptions])

return (
<div className={styles.wrapper}>
<label htmlFor={id} className={styles.label}>GPU system</label>
Expand All @@ -25,10 +36,14 @@ export function GpuSystemInput({ id, value, onChange, gpuOptions }: GpuSystemInp
>
{gpuOptions.length === 0
? <option value={value} disabled>Loading GPU catalog…</option>
: gpuOptions.map(g => (
<option key={g.systemId} value={g.systemId}>
{g.label}{g.vramGb ? ` — ${g.vramGb} GB` : ''}
</option>
: [...vendorGroups.entries()].map(([vendor, gpus]) => (
<optgroup key={vendor} label={vendor.toUpperCase()}>
{gpus.map(g => (
<option key={g.systemId} value={g.systemId}>
{g.label}{g.vramGb ? ` — ${g.vramGb} GB` : ''}
</option>
))}
</optgroup>
))
}
</select>
Expand Down
Loading