mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-09-17 20:31:47 +02:00
ui : model memory-fit estimation
Replace the raw runtime-memory estimate with the app's compatibility check: the smallest real Mac memory tier that fits a model file, budgeted as RAM x 0.75 minus fixed overhead with headroom on the file size. The constants move to lib; the unused runtime-memory estimate is dropped. browser-info's OS detection is exported for reuse. Assisted-by: pi:GLM-5.3-Flash
This commit is contained in:
@@ -46,6 +46,7 @@ export * from './path-display.constants';
|
||||
export * from './model-id.constants';
|
||||
export * from './model-loading.constants';
|
||||
export * from './models-discover.constants';
|
||||
export * from './model-compatibility.constants';
|
||||
export * from './huggingface.constants';
|
||||
export * from './precision.constants';
|
||||
export * from './pwa.constants';
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Model memory-fit constants.
|
||||
*
|
||||
* Mirrors the app's compatibility check (Model+Compatibility.swift):
|
||||
* budget = RAM x RAM_BUDGET_RATIO - RAM_OVERHEAD_MB
|
||||
* weightBytes = fileBytes x QUANT_WEIGHT
|
||||
* a file fits when weightBytes <= budget. Kept here so the estimation util and
|
||||
* any caller share one source.
|
||||
*/
|
||||
|
||||
/** Bytes in one mebibyte (MiB), used to convert a file size to MB. */
|
||||
export const MIB_BYTES = 1_048_576;
|
||||
|
||||
/** MB in one GB. */
|
||||
export const MB_PER_GB = 1024;
|
||||
|
||||
/** Overhead multiplier applied to the file size when estimating weight memory. */
|
||||
export const QUANT_WEIGHT = 1.05;
|
||||
|
||||
/** Share of RAM the app allows the model to occupy. */
|
||||
export const RAM_BUDGET_RATIO = 0.75;
|
||||
|
||||
/** Fixed RAM overhead (MB) reserved for the system and KV cache. */
|
||||
export const RAM_OVERHEAD_MB = 2048;
|
||||
|
||||
/**
|
||||
* Memory tiers (GB) covering the RAM sizes common machines ship with, in
|
||||
* small enough steps that the requirement reads honestly. Device-agnostic on
|
||||
* purpose: the server exposes no host RAM, so the UI presents the tier and
|
||||
* lets the user judge.
|
||||
*/
|
||||
export const MEM_TIERS = [4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024];
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
} from '$lib/constants';
|
||||
import type { ToolExecutionResult } from '$lib/types';
|
||||
|
||||
function detectOs(userAgent: string): string {
|
||||
export function detectOs(userAgent: string): string {
|
||||
for (const [pattern, os] of BROWSER_INFO_OS_UA_PATTERNS) {
|
||||
if (pattern.test(userAgent)) return os;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Detects whether a model's chat template supports tool calling.
|
||||
*
|
||||
* There is no server flag for tool support, so we infer it from the chat
|
||||
* template. A template that accepts a `tools` array or emits tool-call tokens
|
||||
* is treated as tool-capable.
|
||||
*/
|
||||
|
||||
/** Tool-call tokens emitted by the template for assistant tool calls, matched case-insensitively. */
|
||||
const TOOL_CALL_TOKENS = [
|
||||
'tool_call',
|
||||
'tool_calls',
|
||||
'function_call',
|
||||
'tool_use',
|
||||
'<tool',
|
||||
'<|tool'
|
||||
];
|
||||
/** Jinja reference to the `tools` array passed in by the caller. */
|
||||
const JINJA_TOOLS_VAR = /\{\{[^{}]*\btools\b[^{}]*\}\}|\{%[^{}]*\btools\b[^{}]*%\}/i;
|
||||
|
||||
export function detectToolUseSupport(t: string): boolean {
|
||||
if (!t) return false;
|
||||
|
||||
if (JINJA_TOOLS_VAR.test(t)) return true;
|
||||
|
||||
const template = t.toLowerCase();
|
||||
|
||||
return TOOL_CALL_TOKENS.some((token) => template.includes(token));
|
||||
}
|
||||
@@ -343,7 +343,13 @@ export { buildSandboxToolDefinition, SANDBOX_TOOL_DEFINITION } from './sandbox-t
|
||||
export { executeGetDatetimeTool } from './get-datetime';
|
||||
|
||||
// Browser fallback for the server's get_info tool
|
||||
export { executeBrowserInfoTool } from './browser-info';
|
||||
export { detectOs, executeBrowserInfoTool } from './browser-info';
|
||||
|
||||
// Tool-use support detection from a chat template
|
||||
export { detectToolUseSupport } from './chat-template-tool-detector';
|
||||
|
||||
// Model memory estimation
|
||||
export { minMemoryTierGb } from './model-compatibility';
|
||||
|
||||
// Cryptography utilities
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Model memory estimation.
|
||||
*
|
||||
* Mirrors the app's compatibility check (Model+Compatibility.swift): the
|
||||
* runtime budget is RAM x 0.75 minus a fixed overhead, and a file fits when
|
||||
* its size with headroom stays under that budget. The result is the smallest
|
||||
* memory tier that can run the model, so the UI presents an honest machine
|
||||
* requirement instead of a raw file size. Context length and
|
||||
* device-specific budgets are deliberately ignored - callers present the
|
||||
* requirement and let the user judge.
|
||||
*/
|
||||
import {
|
||||
MB_PER_GB,
|
||||
MEM_TIERS,
|
||||
MIB_BYTES,
|
||||
QUANT_WEIGHT,
|
||||
RAM_BUDGET_RATIO,
|
||||
RAM_OVERHEAD_MB
|
||||
} from '$lib/constants';
|
||||
|
||||
/**
|
||||
* Smallest memory tier (GB) that can run a model of the given file size,
|
||||
* or null if nothing fits even the largest tier.
|
||||
*/
|
||||
export function minMemoryTierGb(sizeBytes: number): number | null {
|
||||
if (!sizeBytes) return null;
|
||||
|
||||
const weightMb = (sizeBytes / MIB_BYTES) * QUANT_WEIGHT;
|
||||
|
||||
for (const tier of MEM_TIERS) {
|
||||
const budgetMb = tier * MB_PER_GB * RAM_BUDGET_RATIO - RAM_OVERHEAD_MB;
|
||||
|
||||
if (weightMb <= budgetMb) return tier;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user