|
1 | 1 | import { describe, it, expect } from "vitest"; |
2 | | -import { classifyGpuVendor, computeGpuTopology, detectModelFormat, parseMigInstancesFromNvidiaSmiL, recommendModels, recommendModelsWithML, resolveAutomaticRuntime, type GpuInfo, type GpuTopology, type SystemSpecs } from "./system-specs"; |
| 2 | +import { assessGgufFiles, classifyGpuVendor, computeGpuTopology, detectGgufQuantization, detectModelFormat, parseMigInstancesFromNvidiaSmiL, recommendModels, recommendModelsWithML, resolveAutomaticRuntime, type GpuInfo, type GpuTopology, type SystemSpecs } from "./system-specs"; |
3 | 3 |
|
4 | 4 | function baseTopology(overrides: Partial<GpuTopology> = {}): GpuTopology { |
5 | 5 | return { |
@@ -253,6 +253,41 @@ describe("detectModelFormat", () => { |
253 | 253 | }); |
254 | 254 | }); |
255 | 255 |
|
| 256 | +describe("assessGgufFiles", () => { |
| 257 | + it("extracts common GGUF quantizations without confusing model names", () => { |
| 258 | + expect(detectGgufQuantization("Kimi-Q3_K_L.gguf")).toEqual({ label: "Q3_K_L", bits: 3.69 }); |
| 259 | + expect(detectGgufQuantization("model-IQ2_XXS.gguf").label).toBe("IQ2_XXS"); |
| 260 | + expect(detectGgufQuantization("model-UD-Q4_K_XL.gguf").label).toBe("UD-Q4_K_XL"); |
| 261 | + expect(detectGgufQuantization("model.gguf").label).toBe("GGUF"); |
| 262 | + }); |
| 263 | + |
| 264 | + it("reports a full-GPU fit and a non-zero speed estimate", () => { |
| 265 | + const specs = baseSpecs({ |
| 266 | + freeRAMGB: 32, |
| 267 | + gpu: { name: "RTX 4090", vramGB: 24, vendor: "nvidia" }, |
| 268 | + gpus: [{ name: "RTX 4090", vramGB: 24, vendor: "nvidia" }], |
| 269 | + totalVramGB: 24, |
| 270 | + largestGpuVramGB: 24, |
| 271 | + }); |
| 272 | + const [result] = assessGgufFiles(specs, [{ modelId: "org/model", filename: "model-Q4_K_M.gguf", sizeBytes: 8_000_000_000 }]); |
| 273 | + expect(result.outcome).toBe("Runs fully on GPU"); |
| 274 | + expect(result.fits).toBe(true); |
| 275 | + expect(result.totalRequiredGB).toBeGreaterThan(8); |
| 276 | + expect(result.estimatedTokensPerSecond).toBeGreaterThan(0); |
| 277 | + }); |
| 278 | + |
| 279 | + it("marks an oversized file unsafe and handles missing sizes honestly", () => { |
| 280 | + const [oversized, unknown] = assessGgufFiles(baseSpecs({ totalRAMGB: 16, freeRAMGB: 8 }), [ |
| 281 | + { modelId: "org/model", filename: "huge-Q8_0.gguf", sizeBytes: 80_000_000_000 }, |
| 282 | + { modelId: "org/model", filename: "unknown.gguf", sizeBytes: null }, |
| 283 | + ]); |
| 284 | + expect(oversized.outcome).toBe("Likely out of memory"); |
| 285 | + expect(oversized.estimatedTokensPerSecond).toBe(0); |
| 286 | + expect(unknown.canAssess).toBe(false); |
| 287 | + expect(unknown.fits).toBeNull(); |
| 288 | + }); |
| 289 | +}); |
| 290 | + |
256 | 291 | describe("resolveAutomaticRuntime", () => { |
257 | 292 | const linuxNvidia = { platform: "linux" as const, arch: "x64", gpus: [{ name: "RTX 4090", vramGB: 24, vendor: "nvidia" as const }] }; |
258 | 293 | const linuxAmd = { platform: "linux" as const, arch: "x64", gpus: [{ name: "Radeon RX 7900", vramGB: 24, vendor: "amd" as const }] }; |
|
0 commit comments