Show complete engine disk costs before install (#1728)

Closes #1718.

Adds structured pre-install and measured post-install disk costs, complete sidecar preflight accounting, strict authorization for recursive scans, and localized catalogue details with confidence and deduplication context.
This commit is contained in:
Palash Debnath
2026-09-01 23:49:05 +05:30
committed by GitHub
parent a4f68e4ea0
commit 497d57ee62
37 changed files with 1184 additions and 30 deletions
+1
View File
@@ -10,6 +10,7 @@ the frozen-backend fallback mirror it for their toolchains.
**Highlights**
- Show estimated and measured model, dependency, cache, and temporary disk costs in the engine catalogue (#1718)
- CosyVoice setup guidance now separates downloaded model files from the runtime that makes the engine available.
### Changed
+15
View File
@@ -75,6 +75,21 @@ def list_tts_backends():
return _family_payload("tts", tts_backend)
@router.get(
"/engines/{engine_id}/disk-usage",
dependencies=[Depends(require_admin_action)],
)
def engine_disk_usage(engine_id: str):
"""Measure owned engine bytes only when a catalogue row is opened."""
try:
tts_backend.get_backend_class(engine_id)
except ValueError:
raise HTTPException(status_code=404, detail="Unknown TTS engine")
from services.engine_disk_usage import disk_usage_for
return disk_usage_for(engine_id)
@router.get("/engines/asr")
def list_asr_backends():
return _family_payload("asr", asr_backend)
+230
View File
@@ -0,0 +1,230 @@
"""Structured pre-install and measured disk costs for TTS engines."""
from __future__ import annotations
import os
import threading
import time
from functools import lru_cache
from pathlib import Path
_GIB = 1024**3
_CACHE_TTL_SECONDS = 10.0
_measurement_cache: dict[str, tuple[float, dict]] = {}
_measurement_lock = threading.Lock()
# Catalogue/build estimates. ``None`` is deliberate: unknown costs must stay
# visible instead of being silently treated as zero.
_ESTIMATES: dict[str, dict] = {
"omnivoice": {
"package_download_bytes": None,
"unique_installed_bytes": None,
"potentially_shared_bytes": None,
"temporary_free_bytes": None,
"confidence": "estimated",
"destination": "hf_model_cache",
"deduplication": None,
},
"kittentts": {
"package_download_bytes": None,
"unique_installed_bytes": None,
"potentially_shared_bytes": None,
"temporary_free_bytes": None,
"confidence": "estimated",
"destination": "hf_model_cache",
"deduplication": None,
},
}
_MODEL_REPOS = {
"omnivoice": "k2-fsa/OmniVoice",
"kittentts": "KittenML/kitten-tts-mini-0.8",
}
def _volume_root(path: Path) -> str:
"""Mount point/drive containing a possibly not-yet-created destination."""
try:
current = path.expanduser().resolve()
while not current.exists() and current.parent != current:
current = current.parent
device = current.stat().st_dev
while current.parent != current and current.parent.stat().st_dev == device:
current = current.parent
return str(current)
except OSError:
return "unknown"
def _hf_cache_path() -> Path:
configured = (
os.environ.get("HF_HUB_CACHE")
or os.environ.get("HUGGINGFACE_HUB_CACHE")
or os.environ.get("HF_HOME")
)
return Path(configured) if configured else Path.home() / ".cache" / "huggingface"
@lru_cache(maxsize=None)
def _catalog_model_bytes(engine_id: str) -> int | None:
"""Resolve the weight estimate from config/models.yaml, its source of truth."""
repo_id = _MODEL_REPOS.get(engine_id)
if repo_id is None:
return None
try:
import yaml
catalog_path = Path(__file__).resolve().parents[1] / "config" / "models.yaml"
entries = yaml.safe_load(catalog_path.read_text(encoding="utf-8"))["models"]
model = next(item for item in entries if item["repo_id"] == repo_id)
return round(float(model["size_gb"]) * _GIB)
except (OSError, KeyError, StopIteration, TypeError, ValueError):
return None
def _dir_size(path: Path) -> int:
total = 0
try:
for root, _dirs, files in os.walk(path):
for filename in files:
try:
total += os.path.getsize(os.path.join(root, filename))
except OSError:
continue
except OSError:
return 0
return total
def _sidecar_estimate(engine_id: str) -> dict | None:
try:
from services.sidecar_install import get_spec, managed_root
spec = get_spec(engine_id)
except Exception:
return None
if spec is None:
return None
model_bytes = spec.weights_bytes
dependency_bytes = spec.dependency_bytes
return {
"model_download_bytes": model_bytes,
"package_download_bytes": dependency_bytes,
"unique_installed_bytes": spec.required_bytes,
"potentially_shared_bytes": spec.potentially_shared_bytes,
"temporary_free_bytes": spec.temporary_free_bytes,
"confidence": spec.disk_confidence,
"destination": "engine_data",
"destination_volume": _volume_root(managed_root(spec)),
"deduplication": "uv_same_volume",
}
def estimate_for(engine_id: str) -> dict:
estimate = _sidecar_estimate(engine_id) or _ESTIMATES.get(engine_id)
if estimate is not None:
return {
"model_download_bytes": _catalog_model_bytes(engine_id),
"destination_volume": _volume_root(_hf_cache_path()),
**estimate,
}
return {
"model_download_bytes": None,
"package_download_bytes": None,
"unique_installed_bytes": None,
"potentially_shared_bytes": None,
"temporary_free_bytes": None,
"confidence": "unknown",
"destination": "unknown",
"destination_volume": "unknown",
"deduplication": None,
}
def _measure_sidecar(engine_id: str) -> dict | None:
try:
from services.sidecar_install import get_spec, managed_checkout, managed_root
spec = get_spec(engine_id)
except Exception:
return None
if spec is None:
return None
checkout = managed_checkout(spec)
if not checkout.is_dir():
return None
model = _dir_size(checkout / spec.weights_subdir)
environment = _dir_size(checkout / ".venv")
total = _dir_size(managed_root(spec))
shared_cache = _dir_size(managed_root(spec).parent / ".uv-cache")
return {
"model_bytes": model,
"environment_bytes": environment,
"cache_bytes": shared_cache,
"total_owned_bytes": total,
"confidence": "measured",
}
def _measure_model_cache(engine_id: str) -> dict | None:
repo_id = _MODEL_REPOS.get(engine_id)
if repo_id is None:
return None
try:
from huggingface_hub import scan_cache_dir
repo = next((item for item in scan_cache_dir().repos if item.repo_id == repo_id), None)
except Exception:
return None
if repo is None or repo.size_on_disk <= 0:
return None
size = int(repo.size_on_disk)
return {
"model_bytes": size,
# The model lives in this cache; cache overhead is not separately
# attributable without double-counting the same hardlinked blobs.
"environment_bytes": None,
"cache_bytes": 0,
"total_owned_bytes": size,
"confidence": "measured",
}
def actual_for(engine_id: str) -> dict:
now = time.monotonic()
cached = _measurement_cache.get(engine_id)
if cached and now - cached[0] < _CACHE_TTL_SECONDS:
return dict(cached[1])
# A cache miss can recursively walk a sidecar and the shared uv cache.
# Coalesce concurrent requests so callers cannot multiply that work.
with _measurement_lock:
now = time.monotonic()
cached = _measurement_cache.get(engine_id)
if cached and now - cached[0] < _CACHE_TTL_SECONDS:
return dict(cached[1])
actual = _measure_sidecar(engine_id) or _measure_model_cache(engine_id) or {
"model_bytes": None,
"environment_bytes": None,
"cache_bytes": None,
"total_owned_bytes": None,
"confidence": "unknown",
}
_measurement_cache[engine_id] = (now, actual)
return dict(actual)
def disk_usage_for(engine_id: str) -> dict:
"""Stable API shape consumed by the engine catalogue."""
return {"estimate": estimate_for(engine_id), "actual": actual_for(engine_id)}
def disk_summary_for(engine_id: str) -> dict:
"""Cheap list payload; measurement is deferred until the row is opened."""
return {
"estimate": estimate_for(engine_id),
"actual": {
"model_bytes": None,
"environment_bytes": None,
"cache_bytes": None,
"total_owned_bytes": None,
"confidence": "unknown",
},
}
+40 -2
View File
@@ -116,6 +116,11 @@ class SidecarSpec:
weights_config_names: tuple[str, ...] = ("config.yaml",)
docs_path: str = "docs/engines" # where the manual-install fallback lives
required_bytes: int = 12 * _GIB # conservative source+venv+weights estimate for preflight
weights_bytes: Optional[int] = None
dependency_bytes: Optional[int] = None
potentially_shared_bytes: Optional[int] = None
temporary_free_bytes: Optional[int] = None
disk_confidence: str = "unknown"
# Called after a successful install/uninstall so the engine's memoised
# venv resolution re-probes (import inside the lambda — never at module load).
invalidate: Callable[[], None] = field(default=lambda: None)
@@ -157,6 +162,11 @@ SPECS: dict[str, SidecarSpec] = {
# ~6 GB weights. Deliberately conservative; the preflight subtracts
# whatever a partial install already put on disk.
required_bytes=12 * _GIB,
weights_bytes=6 * _GIB,
dependency_bytes=6 * _GIB,
potentially_shared_bytes=None,
temporary_free_bytes=12 * _GIB,
disk_confidence="estimated",
invalidate=_indextts_invalidate,
installed_probe=_indextts_installed,
),
@@ -312,6 +322,25 @@ def _dir_size_bytes(path: Path) -> int:
return total
def _preserved_install_bytes(spec: SidecarSpec, checkout: Path) -> tuple[int, int]:
"""Return bytes preserved for the final install and dependency peak.
Resumable weights reduce the final download requirement, but they do not
reduce uv's separate environment-build peak. Only source and a usable
existing venv count against that peak.
"""
if not _source_present(spec, checkout):
return 0, 0
weights_dir = checkout / spec.weights_subdir
weights = _dir_size_bytes(weights_dir) if spec.weights_repo_id else 0
venv_dir = checkout / ".venv"
venv = _dir_size_bytes(venv_dir)
source = max(0, _dir_size_bytes(checkout) - weights - venv)
usable_venv = venv if _venv_python(venv_dir).is_file() else 0
return source + usable_venv + weights, source + usable_venv
def disk_free_bytes(path: Path) -> int:
"""Free bytes on the volume backing *path* (nearest existing ancestor).
Never raises; 0 when the volume can't be probed."""
@@ -334,8 +363,17 @@ def disk_space_error(spec: SidecarSpec) -> Optional[str]:
root = managed_root(spec)
# A preserved predecessor is not a partial copy of the new install: the
# upgrade needs its full space until the new sidecar is verified.
already = _dir_size_bytes(managed_checkout(spec))
remaining = max(0, spec.required_bytes - already)
checkout = managed_checkout(spec)
# Credit only bytes the later steps preserve. An invalid layout or revision
# marker makes _step_fetch_source delete the whole checkout.
preserved, dependency_peak_credit = _preserved_install_bytes(spec, checkout)
remaining = max(0, spec.required_bytes - preserved)
if spec.temporary_free_bytes is not None:
# Resumable model weights are unrelated to uv's dependency-build peak.
remaining = max(
remaining,
max(0, spec.temporary_free_bytes - dependency_peak_credit),
)
free = disk_free_bytes(root)
if free <= 0:
return None # can't probe → never block on missing information
+2
View File
@@ -2399,6 +2399,7 @@ def list_backends() -> list[dict]:
# Routing is host-aware but the host caps are constant per process, so probe
# ONCE here and resolve each engine's effective device against the same caps.
from core.device_caps import detect_host_caps
from services.engine_disk_usage import disk_summary_for
from services.engine_evidence import snapshot as execution_snapshot
from services.engine_routing import routing_fields
caps = detect_host_caps()
@@ -2458,6 +2459,7 @@ def list_backends() -> list[dict]:
# in-app (Settings renders an Install button instead of leading
# with the manual setup snippet).
"one_click_install": bid in installable,
"disk_usage": disk_summary_for(bid),
"last_error": _LAST_ERRORS.get(bid),
"isolation_mode": isolation,
"gpu_compat": list(gpu_compat),
+9
View File
@@ -1,5 +1,14 @@
# Engine venvs & disk usage
The Model Catalogue now exposes a structured disk breakdown before install:
model-weight download, package download, unique installed bytes, potentially
shared bytes, temporary free-space requirement, destination volume, and the
estimate confidence. Missing package or deduplication measurements are shown as
unknown rather than zero. Opening an installed engine's disk details measures
its model, environment, shared cache, and app-owned total separately. These
values come from `config/models.yaml` and the sidecar installer specification;
the UI does not maintain its own size table.
Most engines run in-process in VoiceStudio's main environment. A few
(**IndexTTS2**, **MOSS-TTS-v1.5**, **dots.tts**, and any engine whose
dependencies conflict with the parent's `torch`/`transformers` pins) run in a
+5
View File
@@ -2,6 +2,7 @@ import { apiJson, apiPost } from './client';
import type {
AllEnginesResponse,
EngineFamily,
EngineDiskUsage,
EngineHealthResponse,
EngineSelfTestResponse,
SelectEngineResponse,
@@ -72,6 +73,10 @@ export async function getEngineHealth(engineId: string): Promise<EngineHealthRes
return apiJson<EngineHealthResponse>(`/engines/${encodeURIComponent(engineId)}/health`);
}
export async function getEngineDiskUsage(engineId: string): Promise<EngineDiskUsage> {
return apiJson<EngineDiskUsage>(`/engines/${encodeURIComponent(engineId)}/disk-usage`);
}
/**
* Run a bounded, real tiny-synthesis on an AVAILABLE, IN-PROCESS TTS engine
* proves the engine actually emits audio (duration + sample-rate + samples),
+26
View File
@@ -66,6 +66,32 @@ export interface EngineBackend {
// Settings can render a model picker. Absent on every other backend.
curated_models?: CuratedModel[];
active_model_id?: string;
disk_usage?: EngineDiskUsage;
}
export interface EngineDiskEstimate {
model_download_bytes: number | null;
package_download_bytes: number | null;
unique_installed_bytes: number | null;
potentially_shared_bytes: number | null;
temporary_free_bytes: number | null;
confidence: 'exact' | 'measured' | 'estimated' | 'unknown';
destination: string;
destination_volume: string;
deduplication: string | null;
}
export interface EngineDiskActual {
model_bytes: number | null;
environment_bytes: number | null;
cache_bytes: number | null;
total_owned_bytes: number | null;
confidence: 'measured' | 'unknown';
}
export interface EngineDiskUsage {
estimate: EngineDiskEstimate;
actual: EngineDiskActual;
}
// #981 — one of mlx-audio's curated models (see backend
@@ -21,6 +21,7 @@ import {
selfTestEngine,
installSidecarEngine,
getSidecarInstallStatus,
getEngineDiskUsage,
} from '../api/engines';
import { listLoadedModels, unloadLoadedModel } from '../api/system';
import { useAppStore } from '../store';
@@ -49,6 +50,23 @@ function reasonMentionsLicense(reason) {
return /license not accepted/i.test(reason);
}
export function fmtDiskBytes(value, unknownLabel, locale) {
if (value == null) return unknownLabel;
const [divisor, unit, digits] =
value >= 1024 ** 3
? [1024 ** 3, 'gigabyte', 2]
: value >= 1024 ** 2
? [1024 ** 2, 'megabyte', 1]
: [1024, 'kilobyte', 0];
return new Intl.NumberFormat(locale, {
style: 'unit',
unit,
unitDisplay: 'short',
minimumFractionDigits: digits,
maximumFractionDigits: digits,
}).format(value / divisor);
}
/**
* Engine Compatibility Matrix (Plan 02-04 / ENGINE-06).
*
@@ -233,6 +251,7 @@ function normalizeEntry(entry) {
// null/absent on every other backend, which never renders a picker.
curated_models: Array.isArray(entry.curated_models) ? entry.curated_models : null,
active_model_id: entry.active_model_id || null,
disk_usage: entry.disk_usage || null,
};
}
@@ -269,8 +288,9 @@ export default function EngineCompatibilityMatrix({
// One-click sidecar install layer same injection story as the rest.
apiInstallEngine = installSidecarEngine,
apiInstallStatus = getSidecarInstallStatus,
apiGetDiskUsage = getEngineDiskUsage,
}) {
const { t } = useTranslation();
const { t, i18n } = useTranslation();
const [localData, setLocalData] = useState(null);
const [localLoading, setLocalLoading] = useState(true);
const [localError, setLocalError] = useState(null);
@@ -298,6 +318,13 @@ export default function EngineCompatibilityMatrix({
// (one at a time). The panel renders BELOW the row as its own block, so
// sibling rows keep their fixed two-line height and stay aligned.
const [expandedId, setExpandedId] = useState(null);
const [diskByEngine, setDiskByEngine] = useState({});
const diskGenerationRef = useRef(0);
const invalidateDiskUsage = useCallback(() => {
diskGenerationRef.current += 1;
setDiskByEngine({});
}, []);
// Memory residency: engine id its /model/loaded entry (TTS entries and
// sidecars carry engine_id). Advisory load failures leave it empty and
// the matrix renders exactly as before (no residency chips).
@@ -323,6 +350,7 @@ export default function EngineCompatibilityMatrix({
}, [apiListLoadedModels]);
const reload = useCallback(async () => {
invalidateDiskUsage();
if (sharedRefetch) {
const result = await sharedRefetch();
if (result.error) {
@@ -343,7 +371,7 @@ export default function EngineCompatibilityMatrix({
}
}
refreshResidency();
}, [apiListEngines, refreshResidency, sharedRefetch, t]);
}, [apiListEngines, invalidateDiskUsage, refreshResidency, sharedRefetch, t]);
useEffect(() => {
if (isShared) {
@@ -827,15 +855,17 @@ export default function EngineCompatibilityMatrix({
// Unavailable-row detail material for the expansion panel.
// One-click-installable rows always have a panel it hosts the
// install progress and the demoted manual-install fallback.
const hasDiskDetails = activeFamily === 'tts' && !!b.disk_usage;
const hasDetails =
!b.available &&
!!(
b.reason ||
b.install_hint ||
b.last_error ||
b.setup_snippet ||
b.one_click_install
);
hasDiskDetails ||
(!b.available &&
!!(
b.reason ||
b.install_hint ||
b.last_error ||
b.setup_snippet ||
b.one_click_install
));
const install = installByEngine[b.id] || null;
const installJob = install?.job || null;
const installRunning = installJob?.state === 'running';
@@ -860,7 +890,9 @@ export default function EngineCompatibilityMatrix({
variant="subtle"
onClick={() => copySetup(b.id, b.setup_snippet)}
leading={copiedId === b.id ? <Check size={11} /> : <Copy size={11} />}
aria-label={t('engines.copySetup', { engine: b.display_name })}
aria-label={t('engines.copySetup', {
engine: b.display_name,
})}
>
{copiedId === b.id ? t('engines.copied') : t('engines.copy')}
</Button>
@@ -1024,7 +1056,9 @@ export default function EngineCompatibilityMatrix({
value={b.active_model_id || ''}
disabled={!onSelect || !b.available}
onChange={(e) => changeModel(b.id, e.target.value)}
aria-label={t('engines.curatedModelAria', { engine: b.display_name })}
aria-label={t('engines.curatedModelAria', {
engine: b.display_name,
})}
data-testid={`curated-model-select-${b.id}`}
>
{b.curated_models.map((m) => (
@@ -1076,7 +1110,27 @@ export default function EngineCompatibilityMatrix({
aria-expanded={expanded}
aria-controls={panelId}
data-testid={`why-toggle-${b.id}`}
onClick={() => setExpandedId(expanded ? null : b.id)}
onClick={async () => {
if (expanded) {
setExpandedId(null);
return;
}
setExpandedId(b.id);
if (hasDiskDetails && !diskByEngine[b.id]) {
const generation = diskGenerationRef.current;
try {
const usage = await apiGetDiskUsage(b.id);
if (diskGenerationRef.current === generation) {
setDiskByEngine((current) => ({
...current,
[b.id]: usage,
}));
}
} catch {
// Estimates remain useful when measurement is unavailable.
}
}
}}
>
<ChevronRight
size={10}
@@ -1085,7 +1139,7 @@ export default function EngineCompatibilityMatrix({
expanded && 'rotate-90',
)}
/>
{t('engines.whyUnavailable')}
{b.available ? t('engines.diskDetails') : t('engines.whyUnavailable')}
</button>
)}
</span>
@@ -1258,7 +1312,9 @@ export default function EngineCompatibilityMatrix({
loading={installRunning}
leading={!installRunning && <Download size={11} />}
data-testid={`install-${b.id}`}
aria-label={t('engines.installAria', { engine: b.display_name })}
aria-label={t('engines.installAria', {
engine: b.display_name,
})}
>
{installRunning
? t('engines.installing')
@@ -1432,6 +1488,62 @@ export default function EngineCompatibilityMatrix({
{t('engines.lastError', { error: b.last_error })}
</span>
)}
{hasDiskDetails &&
(() => {
const usage = diskByEngine[b.id] || b.disk_usage;
const estimate = usage?.estimate || {};
const actual = usage?.actual || {};
const value = (bytes) =>
fmtDiskBytes(bytes, t('common.unknown'), i18n.resolvedLanguage);
return (
<div
className="engine-matrix__disk mt-[4px] grid grid-cols-[max-content_1fr] gap-x-[12px] gap-y-[2px]"
data-testid={`disk-usage-${b.id}`}
>
<span>{t('engines.diskModelDownload')}</span>
<strong>{value(estimate.model_download_bytes)}</strong>
<span>{t('engines.diskPackageDownload')}</span>
<strong>{value(estimate.package_download_bytes)}</strong>
<span>{t('engines.diskUniqueInstalled')}</span>
<strong>{value(estimate.unique_installed_bytes)}</strong>
<span>{t('engines.diskPotentiallyShared')}</span>
<strong>{value(estimate.potentially_shared_bytes)}</strong>
<span>{t('engines.diskTemporary')}</span>
<strong>{value(estimate.temporary_free_bytes)}</strong>
<span>{t('engines.diskEstimateConfidence')}</span>
<strong>
{t(`engines.diskConfidence_${estimate.confidence || 'unknown'}`)}
</strong>
<span>{t('engines.diskDestination')}</span>
<strong>
{estimate.destination && estimate.destination !== 'unknown'
? t(`engines.diskDestination_${estimate.destination}`)
: t('common.unknown')}
{estimate.destination_volume &&
estimate.destination_volume !== 'unknown' && (
<code className="ml-[6px]">{estimate.destination_volume}</code>
)}
</strong>
<span>{t('engines.diskActualModel')}</span>
<strong>{value(actual.model_bytes)}</strong>
<span>{t('engines.diskActualEnvironment')}</span>
<strong>{value(actual.environment_bytes)}</strong>
<span>{t('engines.diskActualCache')}</span>
<strong>{value(actual.cache_bytes)}</strong>
<span>{t('engines.diskActualTotal')}</span>
<strong>{value(actual.total_owned_bytes)}</strong>
<span>{t('engines.diskActualConfidence')}</span>
<strong>
{t(`engines.diskConfidence_${actual.confidence || 'unknown'}`)}
</strong>
{estimate.deduplication && (
<span className="col-span-2 mt-[2px] text-[color:var(--chrome-fg-muted,#888)]">
{t(`engines.diskDedup_${estimate.deduplication}`)}
</span>
)}
</div>
);
})()}
{/* One-click install progress: per-step states + the
live log tail while the provisioner job runs, error
+ remediation on failure. Poll-driven (1.5 s). */}
@@ -1461,7 +1573,9 @@ export default function EngineCompatibilityMatrix({
: s.state === 'error'
? '[!]'
: '[ ]'}{' '}
{t(`engines.installStep_${s.id}`, { defaultValue: s.id })}
{t(`engines.installStep_${s.id}`, {
defaultValue: s.id,
})}
{s.id === 'fetch_weights' &&
s.state === 'running' &&
installJob.weights_progress?.pct != null &&
@@ -16,6 +16,7 @@ vi.mock('../../api/engines', () => ({
selfTestEngine: vi.fn(),
installSidecarEngine: vi.fn(),
getSidecarInstallStatus: vi.fn(),
getEngineDiskUsage: vi.fn(),
}));
// Residency layer (/model/loaded) mocked so the matrix never hits the
+20
View File
@@ -743,6 +743,26 @@
"engineCompatLabel": "{{family}} توافق المحرك",
"active": "نشط",
"whyUnavailable": "ما الذي يحتاجه",
"diskDetails": "تفاصيل القرص",
"diskModelDownload": "تنزيل النموذج",
"diskPackageDownload": "تنزيل الحزم",
"diskUniqueInstalled": "المساحة المثبتة الفريدة",
"diskPotentiallyShared": "المساحة المحتمل مشاركتها",
"diskTemporary": "المساحة الحرة المؤقتة",
"diskDestination": "الوجهة",
"diskDestination_hf_model_cache": "وحدة تخزين ذاكرة نماذج Hugging Face المؤقتة",
"diskDestination_engine_data": "وحدة تخزين بيانات محركات VoiceStudio",
"diskActualModel": "حجم النموذج الفعلي",
"diskActualEnvironment": "حجم البيئة الفعلي",
"diskActualCache": "حجم الذاكرة المؤقتة المشتركة الفعلي",
"diskActualTotal": "إجمالي المساحة المملوكة الفعلي",
"diskEstimateConfidence": "موثوقية التقدير",
"diskActualConfidence": "موثوقية القياس",
"diskConfidence_exact": "دقيق",
"diskConfidence_measured": "مقاس",
"diskConfidence_estimated": "تقديري",
"diskConfidence_unknown": "غير معروف",
"diskDedup_uv_same_volume": "يزيل uv تكرار ملفات wheel المتطابقة فقط عندما تكون ذاكرته المؤقتة وبيئة المحرك على وحدة التخزين نفسها.",
"sectionReady": "جاهزة للاستخدام",
"sectionMore": "أضف المزيد من المحركات",
"lastError": "الخطأ الأخير: {{error}}",
+20
View File
@@ -743,6 +743,26 @@
"engineCompatLabel": "{{family}} Motorkompatibilität",
"active": "aktiv",
"whyUnavailable": "Was benötigt wird",
"diskDetails": "Speicherplatzdetails",
"diskModelDownload": "Modelldownload",
"diskPackageDownload": "Paketdownload",
"diskUniqueInstalled": "Einzigartig installiert",
"diskPotentiallyShared": "Potenziell gemeinsam genutzt",
"diskTemporary": "Temporärer freier Speicher",
"diskDestination": "Ziel",
"diskDestination_hf_model_cache": "Hugging-Face-Modellcache-Laufwerk",
"diskDestination_engine_data": "VoiceStudio-Engine-Datenlaufwerk",
"diskActualModel": "Tatsächliches Modell",
"diskActualEnvironment": "Tatsächliche Umgebung",
"diskActualCache": "Tatsächlicher gemeinsamer Cache",
"diskActualTotal": "Tatsächlich eigener Gesamtbedarf",
"diskEstimateConfidence": "Schätzgenauigkeit",
"diskActualConfidence": "Messgenauigkeit",
"diskConfidence_exact": "Exakt",
"diskConfidence_measured": "Gemessen",
"diskConfidence_estimated": "Geschätzt",
"diskConfidence_unknown": "Unbekannt",
"diskDedup_uv_same_volume": "uv dedupliziert identische Wheels nur, wenn Cache und Engine-Umgebung auf demselben Laufwerk liegen.",
"sectionReady": "Einsatzbereit",
"sectionMore": "Weitere Motoren hinzufügen",
"lastError": "Letzter Fehler: {{error}}",
+20
View File
@@ -2049,6 +2049,26 @@
"engineCompatLabel": "{{family}} engine compatibility",
"active": "active",
"whyUnavailable": "What it needs",
"diskDetails": "Disk details",
"diskModelDownload": "Model download",
"diskPackageDownload": "Package download",
"diskUniqueInstalled": "Unique installed",
"diskPotentiallyShared": "Potentially shared",
"diskTemporary": "Temporary free space",
"diskDestination": "Destination",
"diskDestination_hf_model_cache": "Hugging Face model-cache volume",
"diskDestination_engine_data": "VoiceStudio engine-data volume",
"diskActualModel": "Actual model",
"diskActualEnvironment": "Actual environment",
"diskActualCache": "Actual shared cache",
"diskActualTotal": "Actual owned total",
"diskEstimateConfidence": "Estimate confidence",
"diskActualConfidence": "Measurement confidence",
"diskConfidence_exact": "Exact",
"diskConfidence_measured": "Measured",
"diskConfidence_estimated": "Estimated",
"diskConfidence_unknown": "Unknown",
"diskDedup_uv_same_volume": "uv deduplicates identical wheels only when its cache and the engine environment are on the same volume.",
"sectionReady": "Ready to use",
"sectionMore": "Add more engines",
"lastError": "Last error: {{error}}",
+20
View File
@@ -743,6 +743,26 @@
"engineCompatLabel": "{{family}} compatibilidad del motor",
"active": "activo",
"whyUnavailable": "Qué necesita",
"diskDetails": "Detalles de disco",
"diskModelDownload": "Descarga del modelo",
"diskPackageDownload": "Descarga de paquetes",
"diskUniqueInstalled": "Instalación exclusiva",
"diskPotentiallyShared": "Posible espacio compartido",
"diskTemporary": "Espacio temporal libre",
"diskDestination": "Destino",
"diskDestination_hf_model_cache": "Volumen de caché de modelos de Hugging Face",
"diskDestination_engine_data": "Volumen de datos de motores de VoiceStudio",
"diskActualModel": "Modelo real",
"diskActualEnvironment": "Entorno real",
"diskActualCache": "Caché compartida real",
"diskActualTotal": "Total propio real",
"diskEstimateConfidence": "Confianza de la estimación",
"diskActualConfidence": "Confianza de la medición",
"diskConfidence_exact": "Exacta",
"diskConfidence_measured": "Medida",
"diskConfidence_estimated": "Estimada",
"diskConfidence_unknown": "Desconocida",
"diskDedup_uv_same_volume": "uv deduplica wheels idénticos solo si su caché y el entorno del motor están en el mismo volumen.",
"sectionReady": "Listos para usar",
"sectionMore": "Añadir más motores",
"lastError": "Último error: {{error}}",
+20
View File
@@ -743,6 +743,26 @@
"engineCompatLabel": "Compatibilité moteur {{family}}",
"active": "actif",
"whyUnavailable": "Ce qu'il lui faut",
"diskDetails": "Détails du disque",
"diskModelDownload": "Téléchargement du modèle",
"diskPackageDownload": "Téléchargement des paquets",
"diskUniqueInstalled": "Installation unique",
"diskPotentiallyShared": "Potentiellement partagé",
"diskTemporary": "Espace libre temporaire",
"diskDestination": "Destination",
"diskDestination_hf_model_cache": "Volume du cache de modèles Hugging Face",
"diskDestination_engine_data": "Volume de données des moteurs VoiceStudio",
"diskActualModel": "Modèle réel",
"diskActualEnvironment": "Environnement réel",
"diskActualCache": "Cache partagé réel",
"diskActualTotal": "Total réellement détenu",
"diskEstimateConfidence": "Fiabilité de lestimation",
"diskActualConfidence": "Fiabilité de la mesure",
"diskConfidence_exact": "Exacte",
"diskConfidence_measured": "Mesurée",
"diskConfidence_estimated": "Estimée",
"diskConfidence_unknown": "Inconnue",
"diskDedup_uv_same_volume": "uv déduplique les wheels identiques uniquement si son cache et lenvironnement du moteur sont sur le même volume.",
"sectionReady": "Prêts à l'emploi",
"sectionMore": "Ajouter d'autres moteurs",
"lastError": "Dernière erreur : {{error}}",
+20
View File
@@ -743,6 +743,26 @@
"engineCompatLabel": "{{family}} इंजन अनुकूलता",
"active": "सक्रिय",
"whyUnavailable": "इसे क्या चाहिए",
"diskDetails": "डिस्क विवरण",
"diskModelDownload": "मॉडल डाउनलोड",
"diskPackageDownload": "पैकेज डाउनलोड",
"diskUniqueInstalled": "अद्वितीय स्थापित आकार",
"diskPotentiallyShared": "संभावित साझा आकार",
"diskTemporary": "अस्थायी खाली स्थान",
"diskDestination": "गंतव्य",
"diskDestination_hf_model_cache": "Hugging Face मॉडल कैश वॉल्यूम",
"diskDestination_engine_data": "VoiceStudio इंजन डेटा वॉल्यूम",
"diskActualModel": "वास्तविक मॉडल",
"diskActualEnvironment": "वास्तविक परिवेश",
"diskActualCache": "वास्तविक साझा कैश",
"diskActualTotal": "वास्तविक स्वामित्व कुल",
"diskEstimateConfidence": "अनुमान की विश्वसनीयता",
"diskActualConfidence": "माप की विश्वसनीयता",
"diskConfidence_exact": "सटीक",
"diskConfidence_measured": "मापा गया",
"diskConfidence_estimated": "अनुमानित",
"diskConfidence_unknown": "अज्ञात",
"diskDedup_uv_same_volume": "uv समान wheels की प्रतियां तभी हटाता है जब उसका कैश और इंजन परिवेश एक ही वॉल्यूम पर हों।",
"sectionReady": "उपयोग के लिए तैयार",
"sectionMore": "और इंजन जोड़ें",
"lastError": "अंतिम त्रुटि: {{error}}",
+20
View File
@@ -743,6 +743,26 @@
"engineCompatLabel": "{{family}} kompatibilitas mesin",
"active": "aktif",
"whyUnavailable": "Apa yang dibutuhkan",
"diskDetails": "Rincian disk",
"diskModelDownload": "Unduhan model",
"diskPackageDownload": "Unduhan paket",
"diskUniqueInstalled": "Terpasang unik",
"diskPotentiallyShared": "Berpotensi dibagikan",
"diskTemporary": "Ruang kosong sementara",
"diskDestination": "Tujuan",
"diskDestination_hf_model_cache": "Volume cache model Hugging Face",
"diskDestination_engine_data": "Volume data mesin VoiceStudio",
"diskActualModel": "Model aktual",
"diskActualEnvironment": "Lingkungan aktual",
"diskActualCache": "Cache bersama aktual",
"diskActualTotal": "Total milik aktual",
"diskEstimateConfidence": "Keyakinan estimasi",
"diskActualConfidence": "Keyakinan pengukuran",
"diskConfidence_exact": "Tepat",
"diskConfidence_measured": "Terukur",
"diskConfidence_estimated": "Perkiraan",
"diskConfidence_unknown": "Tidak diketahui",
"diskDedup_uv_same_volume": "uv mendeduplikasi wheel identik hanya jika cache dan lingkungan mesin berada pada volume yang sama.",
"sectionReady": "Siap digunakan",
"sectionMore": "Tambahkan mesin lainnya",
"lastError": "Kesalahan terakhir: {{error}}",
+20
View File
@@ -743,6 +743,26 @@
"engineCompatLabel": "{{family}} compatibilità motore",
"active": "attivo",
"whyUnavailable": "Cosa serve",
"diskDetails": "Dettagli disco",
"diskModelDownload": "Download del modello",
"diskPackageDownload": "Download dei pacchetti",
"diskUniqueInstalled": "Installazione univoca",
"diskPotentiallyShared": "Potenzialmente condiviso",
"diskTemporary": "Spazio libero temporaneo",
"diskDestination": "Destinazione",
"diskDestination_hf_model_cache": "Volume della cache modelli Hugging Face",
"diskDestination_engine_data": "Volume dei dati dei motori VoiceStudio",
"diskActualModel": "Modello effettivo",
"diskActualEnvironment": "Ambiente effettivo",
"diskActualCache": "Cache condivisa effettiva",
"diskActualTotal": "Totale effettivamente occupato",
"diskEstimateConfidence": "Affidabilità della stima",
"diskActualConfidence": "Affidabilità della misura",
"diskConfidence_exact": "Esatta",
"diskConfidence_measured": "Misurata",
"diskConfidence_estimated": "Stimata",
"diskConfidence_unknown": "Sconosciuta",
"diskDedup_uv_same_volume": "uv deduplica wheel identiche solo quando la cache e lambiente del motore sono sullo stesso volume.",
"sectionReady": "Pronti all'uso",
"sectionMore": "Aggiungi altri motori",
"lastError": "Ultimo errore: {{error}}",
+20
View File
@@ -743,6 +743,26 @@
"engineCompatLabel": "{{family}} エンジンの互換性",
"active": "アクティブな",
"whyUnavailable": "必要なもの",
"diskDetails": "ディスクの詳細",
"diskModelDownload": "モデルのダウンロード",
"diskPackageDownload": "パッケージのダウンロード",
"diskUniqueInstalled": "固有のインストール容量",
"diskPotentiallyShared": "共有可能な容量",
"diskTemporary": "一時的な空き容量",
"diskDestination": "保存先",
"diskDestination_hf_model_cache": "Hugging Face モデルキャッシュのボリューム",
"diskDestination_engine_data": "VoiceStudio エンジンデータのボリューム",
"diskActualModel": "実際のモデル",
"diskActualEnvironment": "実際の環境",
"diskActualCache": "実際の共有キャッシュ",
"diskActualTotal": "実際の専有合計",
"diskEstimateConfidence": "推定の信頼度",
"diskActualConfidence": "測定の信頼度",
"diskConfidence_exact": "正確",
"diskConfidence_measured": "測定済み",
"diskConfidence_estimated": "推定",
"diskConfidence_unknown": "不明",
"diskDedup_uv_same_volume": "uv は、キャッシュとエンジン環境が同じボリュームにある場合のみ、同一の wheel を重複排除します。",
"sectionReady": "すぐに使える",
"sectionMore": "エンジンを追加",
"lastError": "最後のエラー: {{error}}",
+20
View File
@@ -743,6 +743,26 @@
"engineCompatLabel": "{{family}} 엔진 호환성",
"active": "활성",
"whyUnavailable": "필요한 것",
"diskDetails": "디스크 세부 정보",
"diskModelDownload": "모델 다운로드",
"diskPackageDownload": "패키지 다운로드",
"diskUniqueInstalled": "고유 설치 용량",
"diskPotentiallyShared": "공유 가능 용량",
"diskTemporary": "임시 여유 공간",
"diskDestination": "대상",
"diskDestination_hf_model_cache": "Hugging Face 모델 캐시 볼륨",
"diskDestination_engine_data": "VoiceStudio 엔진 데이터 볼륨",
"diskActualModel": "실제 모델",
"diskActualEnvironment": "실제 환경",
"diskActualCache": "실제 공유 캐시",
"diskActualTotal": "실제 소유 합계",
"diskEstimateConfidence": "예상 신뢰도",
"diskActualConfidence": "측정 신뢰도",
"diskConfidence_exact": "정확함",
"diskConfidence_measured": "측정됨",
"diskConfidence_estimated": "예상됨",
"diskConfidence_unknown": "알 수 없음",
"diskDedup_uv_same_volume": "uv는 캐시와 엔진 환경이 같은 볼륨에 있을 때만 동일한 wheel을 중복 제거합니다.",
"sectionReady": "바로 사용 가능",
"sectionMore": "엔진 추가",
"lastError": "마지막 오류: {{error}}",
+20
View File
@@ -743,6 +743,26 @@
"engineCompatLabel": "{{family}} motorcompatibiliteit",
"active": "actief",
"whyUnavailable": "Wat het nodig heeft",
"diskDetails": "Schijfdetails",
"diskModelDownload": "Modeldownload",
"diskPackageDownload": "Pakketdownload",
"diskUniqueInstalled": "Uniek geïnstalleerd",
"diskPotentiallyShared": "Mogelijk gedeeld",
"diskTemporary": "Tijdelijke vrije ruimte",
"diskDestination": "Bestemming",
"diskDestination_hf_model_cache": "Volume voor Hugging Face-modelcache",
"diskDestination_engine_data": "Volume voor VoiceStudio-enginegegevens",
"diskActualModel": "Werkelijk model",
"diskActualEnvironment": "Werkelijke omgeving",
"diskActualCache": "Werkelijke gedeelde cache",
"diskActualTotal": "Werkelijk eigen totaal",
"diskEstimateConfidence": "Betrouwbaarheid schatting",
"diskActualConfidence": "Betrouwbaarheid meting",
"diskConfidence_exact": "Exact",
"diskConfidence_measured": "Gemeten",
"diskConfidence_estimated": "Geschat",
"diskConfidence_unknown": "Onbekend",
"diskDedup_uv_same_volume": "uv dedupliceert identieke wheels alleen als de cache en engineomgeving op hetzelfde volume staan.",
"sectionReady": "Klaar voor gebruik",
"sectionMore": "Meer motoren toevoegen",
"lastError": "Laatste fout: {{error}}",
+20
View File
@@ -743,6 +743,26 @@
"engineCompatLabel": "{{family}} kompatybilność silnika",
"active": "aktywny",
"whyUnavailable": "Czego potrzebuje",
"diskDetails": "Szczegóły dysku",
"diskModelDownload": "Pobieranie modelu",
"diskPackageDownload": "Pobieranie pakietów",
"diskUniqueInstalled": "Unikalnie zainstalowane",
"diskPotentiallyShared": "Potencjalnie współdzielone",
"diskTemporary": "Tymczasowe wolne miejsce",
"diskDestination": "Miejsce docelowe",
"diskDestination_hf_model_cache": "Wolumin pamięci modeli Hugging Face",
"diskDestination_engine_data": "Wolumin danych silników VoiceStudio",
"diskActualModel": "Rzeczywisty model",
"diskActualEnvironment": "Rzeczywiste środowisko",
"diskActualCache": "Rzeczywista współdzielona pamięć podręczna",
"diskActualTotal": "Rzeczywista suma własna",
"diskEstimateConfidence": "Wiarygodność szacunku",
"diskActualConfidence": "Wiarygodność pomiaru",
"diskConfidence_exact": "Dokładne",
"diskConfidence_measured": "Zmierzone",
"diskConfidence_estimated": "Szacowane",
"diskConfidence_unknown": "Nieznane",
"diskDedup_uv_same_volume": "uv deduplikuje identyczne wheels tylko wtedy, gdy pamięć podręczna i środowisko silnika są na tym samym woluminie.",
"sectionReady": "Gotowe do użycia",
"sectionMore": "Dodaj więcej silników",
"lastError": "Ostatni błąd: {{error}}",
+20
View File
@@ -743,6 +743,26 @@
"engineCompatLabel": "{{family}} compatibilidade do motor",
"active": "ativo",
"whyUnavailable": "O que falta",
"diskDetails": "Detalhes do disco",
"diskModelDownload": "Download do modelo",
"diskPackageDownload": "Download de pacotes",
"diskUniqueInstalled": "Instalação exclusiva",
"diskPotentiallyShared": "Possivelmente compartilhado",
"diskTemporary": "Espaço livre temporário",
"diskDestination": "Destino",
"diskDestination_hf_model_cache": "Volume de cache de modelos do Hugging Face",
"diskDestination_engine_data": "Volume de dados dos mecanismos do VoiceStudio",
"diskActualModel": "Modelo real",
"diskActualEnvironment": "Ambiente real",
"diskActualCache": "Cache compartilhado real",
"diskActualTotal": "Total próprio real",
"diskEstimateConfidence": "Confiança da estimativa",
"diskActualConfidence": "Confiança da medição",
"diskConfidence_exact": "Exata",
"diskConfidence_measured": "Medida",
"diskConfidence_estimated": "Estimada",
"diskConfidence_unknown": "Desconhecida",
"diskDedup_uv_same_volume": "O uv deduplica wheels idênticos apenas quando o cache e o ambiente do mecanismo estão no mesmo volume.",
"sectionReady": "Prontos para usar",
"sectionMore": "Adicionar mais motores",
"lastError": "Último erro: {{error}}",
+20
View File
@@ -743,6 +743,26 @@
"engineCompatLabel": "Совместимость с двигателем {{family}}",
"active": "активный",
"whyUnavailable": "Что нужно",
"diskDetails": "Сведения о диске",
"diskModelDownload": "Загрузка модели",
"diskPackageDownload": "Загрузка пакетов",
"diskUniqueInstalled": "Уникально установлено",
"diskPotentiallyShared": "Возможно совместное использование",
"diskTemporary": "Временное свободное место",
"diskDestination": "Назначение",
"diskDestination_hf_model_cache": "Том кэша моделей Hugging Face",
"diskDestination_engine_data": "Том данных движков VoiceStudio",
"diskActualModel": "Фактическая модель",
"diskActualEnvironment": "Фактическое окружение",
"diskActualCache": "Фактический общий кэш",
"diskActualTotal": "Фактический собственный итог",
"diskEstimateConfidence": "Достоверность оценки",
"diskActualConfidence": "Достоверность измерения",
"diskConfidence_exact": "Точно",
"diskConfidence_measured": "Измерено",
"diskConfidence_estimated": "Оценочно",
"diskConfidence_unknown": "Неизвестно",
"diskDedup_uv_same_volume": "uv дедуплицирует одинаковые wheels, только если кэш и окружение движка находятся на одном томе.",
"sectionReady": "Готовы к использованию",
"sectionMore": "Добавить больше двигателей",
"lastError": "Последняя ошибка: {{error}}",
+20
View File
@@ -743,6 +743,26 @@
"engineCompatLabel": "{{family}} motorkompatibilitet",
"active": "aktiv",
"whyUnavailable": "Vad som krävs",
"diskDetails": "Diskdetaljer",
"diskModelDownload": "Modellhämtning",
"diskPackageDownload": "Pakethämtning",
"diskUniqueInstalled": "Unikt installerat",
"diskPotentiallyShared": "Möjligen delat",
"diskTemporary": "Tillfälligt ledigt utrymme",
"diskDestination": "Mål",
"diskDestination_hf_model_cache": "Volym för Hugging Face-modellcache",
"diskDestination_engine_data": "Volym för VoiceStudio-motordata",
"diskActualModel": "Faktisk modell",
"diskActualEnvironment": "Faktisk miljö",
"diskActualCache": "Faktisk delad cache",
"diskActualTotal": "Faktisk egen totalsumma",
"diskEstimateConfidence": "Uppskattningens tillförlitlighet",
"diskActualConfidence": "Mätningens tillförlitlighet",
"diskConfidence_exact": "Exakt",
"diskConfidence_measured": "Uppmätt",
"diskConfidence_estimated": "Uppskattad",
"diskConfidence_unknown": "Okänd",
"diskDedup_uv_same_volume": "uv deduplicerar identiska wheels endast när cachen och motormiljön finns på samma volym.",
"sectionReady": "Redo att använda",
"sectionMore": "Lägg till fler motorer",
"lastError": "Senaste fel: {{error}}",
+20
View File
@@ -743,6 +743,26 @@
"engineCompatLabel": "{{family}} ความเข้ากันได้ของเครื่องยนต์",
"active": "ใช้งานอยู่",
"whyUnavailable": "สิ่งที่ต้องมี",
"diskDetails": "รายละเอียดดิสก์",
"diskModelDownload": "ดาวน์โหลดโมเดล",
"diskPackageDownload": "ดาวน์โหลดแพ็กเกจ",
"diskUniqueInstalled": "พื้นที่ติดตั้งเฉพาะ",
"diskPotentiallyShared": "พื้นที่ที่อาจใช้ร่วมกัน",
"diskTemporary": "พื้นที่ว่างชั่วคราว",
"diskDestination": "ปลายทาง",
"diskDestination_hf_model_cache": "โวลุ่มแคชโมเดล Hugging Face",
"diskDestination_engine_data": "โวลุ่มข้อมูลเอนจิน VoiceStudio",
"diskActualModel": "โมเดลจริง",
"diskActualEnvironment": "สภาพแวดล้อมจริง",
"diskActualCache": "แคชที่ใช้ร่วมกันจริง",
"diskActualTotal": "ยอดรวมที่เป็นเจ้าของจริง",
"diskEstimateConfidence": "ความน่าเชื่อถือของค่าประมาณ",
"diskActualConfidence": "ความน่าเชื่อถือของการวัด",
"diskConfidence_exact": "แม่นยำ",
"diskConfidence_measured": "วัดแล้ว",
"diskConfidence_estimated": "โดยประมาณ",
"diskConfidence_unknown": "ไม่ทราบ",
"diskDedup_uv_same_volume": "uv จะลดข้อมูล wheel ที่ซ้ำกันเฉพาะเมื่อแคชและสภาพแวดล้อมของเอนจินอยู่ในโวลุ่มเดียวกัน",
"sectionReady": "พร้อมใช้งาน",
"sectionMore": "เพิ่มเครื่องยนต์อื่น ๆ",
"lastError": "ข้อผิดพลาดครั้งล่าสุด: {{error}}",
+20
View File
@@ -743,6 +743,26 @@
"engineCompatLabel": "{{family}} motor uyumluluğu",
"active": "aktif",
"whyUnavailable": "Neye ihtiyacı var",
"diskDetails": "Disk ayrıntıları",
"diskModelDownload": "Model indirmesi",
"diskPackageDownload": "Paket indirmesi",
"diskUniqueInstalled": "Benzersiz kurulum",
"diskPotentiallyShared": "Olası paylaşılan alan",
"diskTemporary": "Geçici boş alan",
"diskDestination": "Hedef",
"diskDestination_hf_model_cache": "Hugging Face model önbelleği birimi",
"diskDestination_engine_data": "VoiceStudio motor verileri birimi",
"diskActualModel": "Gerçek model",
"diskActualEnvironment": "Gerçek ortam",
"diskActualCache": "Gerçek paylaşılan önbellek",
"diskActualTotal": "Gerçek sahip olunan toplam",
"diskEstimateConfidence": "Tahmin güvenilirliği",
"diskActualConfidence": "Ölçüm güvenilirliği",
"diskConfidence_exact": "Kesin",
"diskConfidence_measured": "Ölçüldü",
"diskConfidence_estimated": "Tahmini",
"diskConfidence_unknown": "Bilinmiyor",
"diskDedup_uv_same_volume": "uv, aynı wheel dosyalarını yalnızca önbelleği ve motor ortamı aynı birimdeyse tekilleştirir.",
"sectionReady": "Kullanıma hazır",
"sectionMore": "Daha fazla motor ekle",
"lastError": "Son hata: {{error}}",
+20
View File
@@ -743,6 +743,26 @@
"engineCompatLabel": "{{family}} сумісність з двигуном",
"active": "активний",
"whyUnavailable": "Що потрібно",
"diskDetails": "Відомості про диск",
"diskModelDownload": "Завантаження моделі",
"diskPackageDownload": "Завантаження пакетів",
"diskUniqueInstalled": "Унікально встановлено",
"diskPotentiallyShared": "Можливо спільне",
"diskTemporary": "Тимчасове вільне місце",
"diskDestination": "Призначення",
"diskDestination_hf_model_cache": "Том кешу моделей Hugging Face",
"diskDestination_engine_data": "Том даних рушіїв VoiceStudio",
"diskActualModel": "Фактична модель",
"diskActualEnvironment": "Фактичне середовище",
"diskActualCache": "Фактичний спільний кеш",
"diskActualTotal": "Фактичний власний підсумок",
"diskEstimateConfidence": "Достовірність оцінки",
"diskActualConfidence": "Достовірність вимірювання",
"diskConfidence_exact": "Точно",
"diskConfidence_measured": "Виміряно",
"diskConfidence_estimated": "Оцінено",
"diskConfidence_unknown": "Невідомо",
"diskDedup_uv_same_volume": "uv дедуплікує однакові wheels, лише якщо кеш і середовище рушія розташовані на одному томі.",
"sectionReady": "Готові до використання",
"sectionMore": "Додати більше двигунів",
"lastError": "Остання помилка: {{error}}",
+20
View File
@@ -743,6 +743,26 @@
"engineCompatLabel": "{{family}} khả năng tương thích động cơ",
"active": "hoạt động",
"whyUnavailable": "Cần những gì",
"diskDetails": "Chi tiết ổ đĩa",
"diskModelDownload": "Tải mô hình",
"diskPackageDownload": "Tải gói",
"diskUniqueInstalled": "Dung lượng cài đặt riêng",
"diskPotentiallyShared": "Có thể dùng chung",
"diskTemporary": "Dung lượng trống tạm thời",
"diskDestination": "Đích",
"diskDestination_hf_model_cache": "Ổ đĩa bộ nhớ đệm mô hình Hugging Face",
"diskDestination_engine_data": "Ổ đĩa dữ liệu động cơ VoiceStudio",
"diskActualModel": "Mô hình thực tế",
"diskActualEnvironment": "Môi trường thực tế",
"diskActualCache": "Bộ nhớ đệm dùng chung thực tế",
"diskActualTotal": "Tổng dung lượng sở hữu thực tế",
"diskEstimateConfidence": "Độ tin cậy của ước tính",
"diskActualConfidence": "Độ tin cậy của phép đo",
"diskConfidence_exact": "Chính xác",
"diskConfidence_measured": "Đã đo",
"diskConfidence_estimated": "Ước tính",
"diskConfidence_unknown": "Không xác định",
"diskDedup_uv_same_volume": "uv chỉ khử trùng lặp các wheel giống nhau khi bộ nhớ đệm và môi trường động cơ nằm trên cùng một ổ đĩa.",
"sectionReady": "Sẵn sàng sử dụng",
"sectionMore": "Thêm động cơ khác",
"lastError": "Lỗi cuối cùng: {{error}}",
+20
View File
@@ -700,6 +700,26 @@
"engineCompatLabel": "{{family}} 引擎兼容性",
"active": "当前",
"whyUnavailable": "所需条件",
"diskDetails": "磁盘详情",
"diskModelDownload": "模型下载",
"diskPackageDownload": "软件包下载",
"diskUniqueInstalled": "独占安装空间",
"diskPotentiallyShared": "可能共享的空间",
"diskTemporary": "临时可用空间",
"diskDestination": "目标位置",
"diskDestination_hf_model_cache": "Hugging Face 模型缓存卷",
"diskDestination_engine_data": "VoiceStudio 引擎数据卷",
"diskActualModel": "实际模型空间",
"diskActualEnvironment": "实际环境空间",
"diskActualCache": "实际共享缓存",
"diskActualTotal": "实际独占总量",
"diskEstimateConfidence": "估算置信度",
"diskActualConfidence": "测量置信度",
"diskConfidence_exact": "精确",
"diskConfidence_measured": "已测量",
"diskConfidence_estimated": "估算",
"diskConfidence_unknown": "未知",
"diskDedup_uv_same_volume": "仅当 uv 缓存和引擎环境位于同一卷时,uv 才能对相同的 wheel 去重。",
"sectionReady": "即可使用",
"sectionMore": "添加更多引擎",
"lastError": "最后一个错误:{{error}}",
+20
View File
@@ -743,6 +743,26 @@
"engineCompatLabel": "{{family}} 引擎相容性",
"active": "活躍的",
"whyUnavailable": "所需條件",
"diskDetails": "磁碟詳細資料",
"diskModelDownload": "模型下載",
"diskPackageDownload": "套件下載",
"diskUniqueInstalled": "獨占安裝空間",
"diskPotentiallyShared": "可能共用的空間",
"diskTemporary": "暫時可用空間",
"diskDestination": "目的地",
"diskDestination_hf_model_cache": "Hugging Face 模型快取磁碟區",
"diskDestination_engine_data": "VoiceStudio 引擎資料磁碟區",
"diskActualModel": "實際模型空間",
"diskActualEnvironment": "實際環境空間",
"diskActualCache": "實際共用快取",
"diskActualTotal": "實際獨占總量",
"diskEstimateConfidence": "估算可信度",
"diskActualConfidence": "測量可信度",
"diskConfidence_exact": "精確",
"diskConfidence_measured": "已測量",
"diskConfidence_estimated": "估算",
"diskConfidence_unknown": "未知",
"diskDedup_uv_same_volume": "只有在 uv 快取與引擎環境位於同一磁碟區時,uv 才能對相同的 wheel 去除重複。",
"sectionReady": "隨時可用",
"sectionMore": "新增更多引擎",
"lastError": "最後一個錯誤:{{error}}",
@@ -1,6 +1,6 @@
import React from 'react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { fireEvent, render, screen, waitFor, within } from '@testing-library/react';
import { act, fireEvent, render, screen, waitFor, within } from '@testing-library/react';
// Mock the toast import the component depends on keeps the test free
// of side-effect side-channels (toast() schedules timers we don't want).
@@ -19,6 +19,7 @@ vi.mock('../api/system', () => ({
import EngineCompatibilityMatrix, {
FORCE_WAIT_TIMEOUT_MS,
fmtDiskBytes,
} from '../components/EngineCompatibilityMatrix';
/** Build a minimal AllEnginesResponse with the three rows the plan calls for. */
@@ -69,6 +70,10 @@ describe('EngineCompatibilityMatrix', () => {
vi.useRealTimers();
});
it('formats disk sizes with the active locale', () => {
expect(fmtDiskBytes(1.5 * 1024 ** 3, 'unknown', 'de-DE')).toMatch(/^1,50\sGB$/u);
});
it('lists available engines first, keeping registration order inside each group', async () => {
// The fixture is deliberately interleaved (available, UNavailable,
// available). A matrix that renders it in payload order buries a usable
@@ -139,6 +144,100 @@ describe('EngineCompatibilityMatrix', () => {
]);
});
it('shows separate estimates and measured disk categories on demand', async () => {
const response = makeEnginesResponse();
response.tts.backends[0].disk_usage = {
estimate: {
model_download_bytes: 2 * 1024 ** 3,
package_download_bytes: null,
unique_installed_bytes: 3 * 1024 ** 3,
potentially_shared_bytes: null,
temporary_free_bytes: 4 * 1024 ** 3,
destination: 'hf_model_cache',
confidence: 'estimated',
deduplication: null,
},
actual: {},
};
const apiGetDiskUsage = vi.fn().mockResolvedValue({
estimate: response.tts.backends[0].disk_usage.estimate,
actual: {
model_bytes: 1024 ** 3,
environment_bytes: null,
cache_bytes: 0,
total_owned_bytes: 1024 ** 3,
confidence: 'measured',
},
});
render(
<EngineCompatibilityMatrix
family="tts"
apiListEngines={vi.fn().mockResolvedValue(response)}
apiGetEngineHealth={vi.fn()}
apiGetDiskUsage={apiGetDiskUsage}
/>,
);
await screen.findByText('OmniVoice (test)');
fireEvent.click(screen.getByRole('button', { name: 'Disk details' }));
await waitFor(() => expect(apiGetDiskUsage).toHaveBeenCalledWith('omnivoice'));
const details = await screen.findByTestId('disk-usage-omnivoice');
expect(within(details).getByText('Model download')).toBeInTheDocument();
expect(within(details).getByText('Package download')).toBeInTheDocument();
expect(within(details).getByText('Actual environment')).toBeInTheDocument();
expect(within(details).getByText('Estimate confidence')).toBeInTheDocument();
expect(within(details).getByText('Estimated')).toBeInTheDocument();
expect(within(details).getByText('Measurement confidence')).toBeInTheDocument();
expect(within(details).getByText('Measured')).toBeInTheDocument();
expect(within(details).getAllByText('1.00 GB')).toHaveLength(2);
expect(within(details).getAllByText('unknown').length).toBeGreaterThan(0);
});
it('ignores a disk measurement that finishes after engine data reloads', async () => {
const response = makeEnginesResponse();
response.tts.backends[0].disk_usage = {
estimate: {
model_download_bytes: 2 * 1024 ** 3,
confidence: 'estimated',
},
actual: {},
};
let resolveMeasurement;
const apiGetDiskUsage = vi.fn(() => new Promise((resolve) => (resolveMeasurement = resolve)));
const apiListEngines = vi.fn().mockResolvedValue(response);
const view = render(
<EngineCompatibilityMatrix
family="tts"
reloadToken={0}
apiListEngines={apiListEngines}
apiGetEngineHealth={vi.fn()}
apiGetDiskUsage={apiGetDiskUsage}
/>,
);
await screen.findByText('OmniVoice (test)');
fireEvent.click(screen.getByRole('button', { name: 'Disk details' }));
await waitFor(() => expect(apiGetDiskUsage).toHaveBeenCalledOnce());
view.rerender(
<EngineCompatibilityMatrix
family="tts"
reloadToken={1}
apiListEngines={apiListEngines}
apiGetEngineHealth={vi.fn()}
apiGetDiskUsage={apiGetDiskUsage}
/>,
);
await waitFor(() => expect(apiListEngines).toHaveBeenCalledTimes(2));
await act(async () => {
resolveMeasurement({
estimate: response.tts.backends[0].disk_usage.estimate,
actual: { model_bytes: 9 * 1024 ** 3, confidence: 'measured' },
});
});
expect(screen.queryByText('9.00 GB')).not.toBeInTheDocument();
});
it('shows isolation_mode badge per row (subprocess for IndexTTS, in-process for the others)', async () => {
const apiListEngines = vi.fn().mockResolvedValue(makeEnginesResponse());
render(
@@ -247,7 +346,9 @@ describe('EngineCompatibilityMatrix', () => {
await waitFor(() => screen.getByText('IndexTTS2 (test)'));
const indexRow = screen.getByText('IndexTTS2 (test)').closest('[role="row"]');
const testBtn = within(indexRow).getByRole('button', { name: /test indextts2/i });
const testBtn = within(indexRow).getByRole('button', {
name: /test indextts2/i,
});
fireEvent.click(testBtn);
await waitFor(() => {
@@ -279,7 +380,9 @@ describe('EngineCompatibilityMatrix', () => {
await waitFor(() => screen.getByText('IndexTTS2 (test)'));
const indexRow = screen.getByText('IndexTTS2 (test)').closest('[role="row"]');
const testBtn = within(indexRow).getByRole('button', { name: /test indextts2/i });
const testBtn = within(indexRow).getByRole('button', {
name: /test indextts2/i,
});
fireEvent.click(testBtn);
await waitFor(() => {
@@ -291,7 +394,12 @@ describe('EngineCompatibilityMatrix', () => {
expect(apiGetEngineHealth).toHaveBeenCalledTimes(1);
// Release the promise so the test doesn't leak a pending microtask.
resolveHealth({ id: 'indextts2', ok: true, message: 'pong', latency_ms: 50 });
resolveHealth({
id: 'indextts2',
ok: true,
message: 'pong',
latency_ms: 50,
});
});
// #21 routing display
@@ -336,7 +444,11 @@ describe('EngineCompatibilityMatrix', () => {
routing_reason: 'requires cuda; this host has cpu',
}),
// Legacy payload: no routing_* keys render exactly as before.
base({ id: 'legacy', display_name: 'Legacy TTS', gpu_compat: ['cpu'] }),
base({
id: 'legacy',
display_name: 'Legacy TTS',
gpu_compat: ['cpu'],
}),
],
},
asr: { active: '', backends: [] },
@@ -482,9 +594,12 @@ describe('EngineCompatibilityMatrix', () => {
// P3-B: in-process health check reads as a liveness/deps check
it('labels an in-process health check "deps OK" while subprocess shows real ms', async () => {
const apiListEngines = vi.fn().mockResolvedValue(makeEnginesResponse());
const apiGetEngineHealth = vi
.fn()
.mockResolvedValue({ id: 'omnivoice', ok: true, message: 'import ok', latency_ms: 0 });
const apiGetEngineHealth = vi.fn().mockResolvedValue({
id: 'omnivoice',
ok: true,
message: 'import ok',
latency_ms: 0,
});
render(
<EngineCompatibilityMatrix
family="tts"
@@ -598,7 +713,9 @@ describe('EngineCompatibilityMatrix', () => {
expect(screen.queryByText('Supertonic-3 — License Acceptance')).not.toBeInTheDocument();
fireEvent.click(
screen.getByRole('button', { name: /review and accept supertonic-3 license/i }),
screen.getByRole('button', {
name: /review and accept supertonic-3 license/i,
}),
);
await waitFor(() => {
expect(screen.getByText('Supertonic-3 — License Acceptance')).toBeInTheDocument();
@@ -634,7 +751,11 @@ describe('EngineCompatibilityMatrix', () => {
);
await waitFor(() => screen.getByText('PocketTTS'));
fireEvent.click(screen.getByRole('button', { name: /review and accept pockettts license/i }));
fireEvent.click(
screen.getByRole('button', {
name: /review and accept pockettts license/i,
}),
);
await waitFor(() => {
expect(screen.getByText('PocketTTS License Acceptance')).toBeInTheDocument();
expect(screen.getByText('Review the access conditions')).toBeInTheDocument();
@@ -829,7 +950,11 @@ describe('EngineCompatibilityMatrix', () => {
label: 'Kokoro (default, fast)',
repo_id: 'mlx-community/Kokoro-82M-bf16',
},
{ key: 'csm', label: 'CSM (voice cloning)', repo_id: 'mlx-community/csm-1b-8bit' },
{
key: 'csm',
label: 'CSM (voice cloning)',
repo_id: 'mlx-community/csm-1b-8bit',
},
{
key: 'outetts',
label: 'OuteTTS',
@@ -173,6 +173,9 @@ def test_list_backends_shape(registry_sandbox):
# below the floor gets a caveat in `routing_reason` BEFORE it spends
# the full compute budget finding out its card is too small.
"min_vram_gb",
# Structured estimated/measured storage costs for pre-install
# decisions and post-install accounting (#1718).
"disk_usage",
# Sanitized actual-vs-declared provider/device evidence (#1717).
"execution_evidence",
}
+1
View File
@@ -89,6 +89,7 @@ GET /engines/sidecar/{engine_id}/install/status
GET /engines/sonitranslate/status
GET /engines/translation
GET /engines/tts
GET /engines/{engine_id}/disk-usage
GET /engines/{engine_id}/health
GET /export/history
GET /gallery/categories
+1
View File
@@ -125,6 +125,7 @@ def test_managed_sidecar_install_stays_desktop_only():
@pytest.mark.parametrize(
("filename", "function_name"),
[
("engines.py", "engine_disk_usage"),
("engines.py", "engine_health"),
("settings.py", "list_llm_provider_models"),
("system.py", "system_diagnose"),
+113
View File
@@ -0,0 +1,113 @@
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
from threading import Event
import pytest
@pytest.fixture
def disk_modules():
from services import engine_disk_usage
from services.sidecar_install import SPECS
return engine_disk_usage, SPECS
def test_in_process_model_exposes_unknown_dependency_cost_explicitly(monkeypatch, disk_modules):
engine_disk_usage, _ = disk_modules
monkeypatch.setattr(engine_disk_usage, "_measure_model_cache", lambda _engine_id: None)
usage = engine_disk_usage.disk_usage_for("omnivoice")
assert usage["estimate"]["model_download_bytes"] > 0
assert usage["estimate"]["package_download_bytes"] is None
assert usage["estimate"]["confidence"] == "estimated"
assert usage["estimate"]["destination_volume"]
def test_lightweight_optional_engine_has_weight_estimate_not_fake_package_zero(monkeypatch, disk_modules):
engine_disk_usage, _ = disk_modules
monkeypatch.setattr(engine_disk_usage, "_measure_model_cache", lambda _engine_id: None)
usage = engine_disk_usage.disk_usage_for("kittentts")
assert usage["estimate"]["model_download_bytes"] == round(0.08 * 1024**3)
assert usage["estimate"]["package_download_bytes"] is None
def test_separate_torch_sidecar_uses_installer_build_metadata(monkeypatch, tmp_path, disk_modules):
engine_disk_usage, SPECS = disk_modules
spec = SPECS["indextts2"]
monkeypatch.setattr("services.sidecar_install.DATA_DIR", tmp_path)
usage = engine_disk_usage.disk_usage_for(spec.engine_id)
assert usage["estimate"]["model_download_bytes"] == spec.weights_bytes
assert usage["estimate"]["package_download_bytes"] == spec.dependency_bytes
assert usage["estimate"]["unique_installed_bytes"] == spec.required_bytes
assert usage["estimate"]["temporary_free_bytes"] == spec.temporary_free_bytes
assert usage["estimate"]["deduplication"] == "uv_same_volume"
def test_installed_sidecar_reports_separate_measured_categories(monkeypatch, tmp_path, disk_modules):
engine_disk_usage, SPECS = disk_modules
spec = SPECS["indextts2"]
monkeypatch.setattr("services.sidecar_install.DATA_DIR", tmp_path)
checkout = tmp_path / "engines" / spec.engine_id / spec.checkout_dirname
for relative, payload in (
(Path(spec.weights_subdir) / "model.bin", b"weights"),
(Path(".venv") / "package.py", b"environment"),
(Path("source.py"), b"source"),
):
path = checkout / relative
path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes(payload)
cache = tmp_path / "engines" / ".uv-cache" / "wheel"
cache.parent.mkdir(parents=True)
cache.write_bytes(b"shared")
engine_disk_usage._measurement_cache.clear()
actual = engine_disk_usage.actual_for(spec.engine_id)
assert actual["model_bytes"] == len(b"weights")
assert actual["environment_bytes"] == len(b"environment")
assert actual["cache_bytes"] == len(b"shared")
assert actual["total_owned_bytes"] == len(b"weights") + len(b"environment") + len(b"source")
assert actual["confidence"] == "measured"
def test_disk_measurement_route_rejects_unknown_engine(monkeypatch):
from api.routers import engines
from fastapi import HTTPException
def unknown_backend(_engine_id):
raise ValueError("unknown")
monkeypatch.setattr(
engines.tts_backend,
"get_backend_class",
unknown_backend,
)
with pytest.raises(HTTPException) as caught:
engines.engine_disk_usage("unknown")
assert caught.value.status_code == 404
def test_concurrent_disk_measurements_are_coalesced(monkeypatch, disk_modules):
engine_disk_usage, _ = disk_modules
calls = 0
entered = Event()
release = Event()
def measure(_engine_id):
nonlocal calls
calls += 1
entered.set()
assert release.wait(timeout=1)
return {"total_owned_bytes": 7, "confidence": "measured"}
engine_disk_usage._measurement_cache.clear()
monkeypatch.setattr(engine_disk_usage, "_measure_sidecar", measure)
with ThreadPoolExecutor(max_workers=2) as pool:
first = pool.submit(engine_disk_usage.actual_for, "coalesce")
assert entered.wait(timeout=1)
second = pool.submit(engine_disk_usage.actual_for, "coalesce")
assert not second.done()
release.set()
results = [first.result(), second.result()]
assert calls == 1
assert [result["total_owned_bytes"] for result in results] == [7, 7]
+51 -1
View File
@@ -195,12 +195,62 @@ def test_disk_preflight_subtracts_partial_install(monkeypatch, tmp_path):
# only the remainder (+headroom) must fit, so a resume isn't blocked.
spec = _mk_spec(required_bytes=1 * _GIB)
root = si.managed_root(spec)
checkout = si.managed_checkout(spec)
root.mkdir(parents=True)
monkeypatch.setattr(si, "_dir_size_bytes", lambda p: int(0.9 * _GIB))
monkeypatch.setattr(si, "_source_present", lambda _spec, _checkout: True)
monkeypatch.setattr(
si,
"_dir_size_bytes",
lambda path: int(0.9 * _GIB) if path == checkout else 0,
)
monkeypatch.setattr(si, "disk_free_bytes", lambda p: (si.MIN_FREE_GB + 1) * _GIB)
assert si.disk_space_error(spec) is None
def test_disk_preflight_subtracts_partial_install_from_peak_requirement(monkeypatch):
spec = _mk_spec(required_bytes=1 * _GIB, temporary_free_bytes=4 * _GIB)
checkout = si.managed_checkout(spec)
monkeypatch.setattr(si, "_source_present", lambda _spec, _checkout: True)
monkeypatch.setattr(
si,
"_dir_size_bytes",
lambda path: int(0.9 * _GIB) if path == checkout else 0,
)
monkeypatch.setattr(si, "disk_free_bytes", lambda _path: (si.MIN_FREE_GB + 2) * _GIB)
assert "3.1 GB" in si.disk_space_error(spec)
def test_disk_preflight_does_not_credit_weights_against_dependency_peak(monkeypatch):
spec = _mk_spec(
required_bytes=12 * _GIB,
temporary_free_bytes=12 * _GIB,
weights_repo_id="Example/Weights",
)
checkout = si.managed_checkout(spec)
weights = checkout / spec.weights_subdir
monkeypatch.setattr(si, "_source_present", lambda _spec, _checkout: True)
monkeypatch.setattr(
si,
"_dir_size_bytes",
lambda path: 7 * _GIB if path == checkout else 6 * _GIB if path == weights else 0,
)
monkeypatch.setattr(si, "disk_free_bytes", lambda _path: (si.MIN_FREE_GB + 10) * _GIB)
assert "11.0 GB" in si.disk_space_error(spec)
def test_disk_preflight_does_not_credit_checkout_that_fetch_will_delete(monkeypatch):
spec = _mk_spec(required_bytes=4 * _GIB, source_revision="new-revision")
checkout = si.managed_checkout(spec)
checkout.mkdir(parents=True)
(checkout / "pyproject.toml").write_text("[project]\nname='fake'\n")
(checkout / si._SOURCE_REVISION_MARKER).write_text("stale-revision\n")
monkeypatch.setattr(si, "_dir_size_bytes", lambda _path: 3 * _GIB)
monkeypatch.setattr(si, "disk_free_bytes", lambda _path: (si.MIN_FREE_GB + 2) * _GIB)
assert "4.0 GB" in si.disk_space_error(spec)
def test_missing_uv_is_actionable(monkeypatch):
spec = _mk_spec()
monkeypatch.setattr(si, "_locate_uv", lambda: None)