@openspecui/server 3.11.0 → 3.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BatchTranslateInputSchema, CliExecutor, CodeEditorThemeSchema, ConfigManager, CustomSoundHashSchema, DASHBOARD_METRIC_KEYS, DashboardConfigSchema, DocumentTranslationConfigSchema, GitConfigSchema, GlobalSettingsManager, LocalModelAssetStateSchema, MarkdownParser, NotificationPublishInputSchema, NotificationSettingsSchema, OPENSPECUI_HOOKS_VERSION, OpenSpecAdapter, OpenSpecUIGlobalSettingsSchema, OpenSpecWatcher, OpsxConfigSchema, OpsxKernel, PtyClientMessageSchema, ReactiveContext, ServiceTranslationEngineIdSchema, TRANSLATION_ENGINE_MANIFESTS, TerminalConfigSchema, TerminalControlParser, TerminalRendererEngineSchema, TranslationCacheReadInputSchema, TranslationCacheSettingsSchema, TranslationCacheWriteInputSchema, TranslationEngineIdSchema, TranslationLocalSettingsSchema, TranslationOpenAISettingsSchema, buildBackendHealthPayload, buildLocalDownloadPlanFromRepositoryFiles, clearCache, getAllTools, getAvailableTools, getConfiguredTools, getDefaultCliCommandString, getDetectedProjectTools, getOpsxEntityRootRelativePath, getToolInitStates, getWatcherRuntimeStatus, inferFileMime, inferFilePreviewKind, initWatcherPool, isWatcherPoolInitialized, normalizeOpsxEntityPath, parseOpsxEntityMetadata, parseOpsxSchemaDetail, resolveTerminalShellDefaults, selectLocalDownloadGroup, sniffGlobalCli, subscribeWatcherRuntimeStatus, terminalNotificationEventToPublishInput } from "@openspecui/core";
|
|
1
|
+
import { BatchTranslateInputSchema, CliExecutor, CodeEditorThemeSchema, ConfigManager, CustomSoundHashSchema, DASHBOARD_METRIC_KEYS, DashboardConfigSchema, DocumentTranslationConfigSchema, GitConfigSchema, GlobalSettingsManager, LocalModelAssetStateSchema, LocalModelLifecycleFileStateSchema, LocalModelLifecycleGroupStateSchema, LocalModelProfileManifestSchema, MarkdownParser, NotificationPublishInputSchema, NotificationSettingsSchema, OPENSPECUI_HOOKS_VERSION, OpenSpecAdapter, OpenSpecUIGlobalSettingsSchema, OpenSpecWatcher, OpsxConfigSchema, OpsxKernel, PtyClientMessageSchema, ReactiveContext, ServiceTranslationEngineIdSchema, TRANSLATION_ENGINE_MANIFESTS, TerminalConfigSchema, TerminalControlParser, TerminalRendererEngineSchema, TranslationCacheReadInputSchema, TranslationCacheSettingsSchema, TranslationCacheWriteInputSchema, TranslationEngineIdSchema, TranslationLocalSettingsSchema, TranslationOpenAISettingsSchema, buildBackendHealthPayload, buildLocalDownloadPlanFromRepositoryFiles, checkLocalDirectionalModelLanguagePair, clearCache, getAllTools, getAvailableTools, getConfiguredTools, getDefaultCliCommandString, getDetectedProjectTools, getOpsxEntityRootRelativePath, getToolInitStates, getWatcherRuntimeStatus, inferFileMime, inferFilePreviewKind, initWatcherPool, isWatcherPoolInitialized, normalizeOpsxEntityPath, parseOpsxEntityMetadata, parseOpsxSchemaDetail, resolveTerminalShellDefaults, selectLocalDownloadGroup, sniffGlobalCli, subscribeWatcherRuntimeStatus, terminalNotificationEventToPublishInput } from "@openspecui/core";
|
|
2
2
|
import { basename, dirname, extname, join, matchesGlob, relative, resolve, sep } from "node:path";
|
|
3
3
|
import { access, copyFile, lstat, mkdir, open, readFile, readlink, realpath, rename, rm, stat, symlink, unlink, writeFile } from "node:fs/promises";
|
|
4
4
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
@@ -16,7 +16,7 @@ import { homedir, platform } from "node:os";
|
|
|
16
16
|
import { EventEmitter } from "node:events";
|
|
17
17
|
import { execFile } from "node:child_process";
|
|
18
18
|
import { promisify } from "node:util";
|
|
19
|
-
import { downloadFile, fileDownloadInfo, listFiles } from "@huggingface/hub";
|
|
19
|
+
import { downloadFile, fileDownloadInfo, listFiles, modelInfo } from "@huggingface/hub";
|
|
20
20
|
import { observable } from "@trpc/server/observable";
|
|
21
21
|
import { z } from "zod";
|
|
22
22
|
import { Agent, EnvHttpProxyAgent, setGlobalDispatcher } from "undici";
|
|
@@ -357,7 +357,7 @@ function normalizeHooksModule(moduleValue) {
|
|
|
357
357
|
onRunWorkflow: isOnRunWorkflowHook(record.onRunWorkflow) ? record.onRunWorkflow : isOnRunWorkflowHook(defaultRecord.onRunWorkflow) ? defaultRecord.onRunWorkflow : isOnRunWorkflowHook(moduleExportsRecord.onRunWorkflow) ? moduleExportsRecord.onRunWorkflow : void 0
|
|
358
358
|
};
|
|
359
359
|
}
|
|
360
|
-
async function pathExists$
|
|
360
|
+
async function pathExists$1(path) {
|
|
361
361
|
try {
|
|
362
362
|
await access(path);
|
|
363
363
|
return true;
|
|
@@ -386,8 +386,8 @@ var ProjectHookRuntime = class {
|
|
|
386
386
|
await Promise.allSettled(callbacks.map((cleanup) => cleanup()));
|
|
387
387
|
}
|
|
388
388
|
async loadFresh() {
|
|
389
|
-
if (!await pathExists$
|
|
390
|
-
const { tsImport } = await import("
|
|
389
|
+
if (!await pathExists$1(this.hooksPath)) return {};
|
|
390
|
+
const { tsImport } = await import("./api-C9I67iTa.mjs");
|
|
391
391
|
return normalizeHooksModule(await tsImport(`${pathToFileURL(this.hooksPath).href}?t=${Date.now()}`, { parentURL: pathToFileURL(this.hooksPath).href }));
|
|
392
392
|
}
|
|
393
393
|
};
|
|
@@ -799,7 +799,7 @@ function parseRelatedChanges(paths) {
|
|
|
799
799
|
continue;
|
|
800
800
|
}
|
|
801
801
|
}
|
|
802
|
-
return [...related].sort((a, b) => a.localeCompare(b));
|
|
802
|
+
return [...related].sort((a$1, b) => a$1.localeCompare(b));
|
|
803
803
|
}
|
|
804
804
|
async function resolveDefaultBranch(projectDir, runGit) {
|
|
805
805
|
const remoteHead = await runGit(projectDir, [
|
|
@@ -1067,9 +1067,9 @@ async function buildDashboardGitSnapshot(options) {
|
|
|
1067
1067
|
maxCommitEntries,
|
|
1068
1068
|
readPathTimestampMs
|
|
1069
1069
|
})));
|
|
1070
|
-
worktrees.sort((a, b) => {
|
|
1071
|
-
if (a.isCurrent !== b.isCurrent) return a.isCurrent ? -1 : 1;
|
|
1072
|
-
return a.branchName.localeCompare(b.branchName);
|
|
1070
|
+
worktrees.sort((a$1, b) => {
|
|
1071
|
+
if (a$1.isCurrent !== b.isCurrent) return a$1.isCurrent ? -1 : 1;
|
|
1072
|
+
return a$1.branchName.localeCompare(b.branchName);
|
|
1073
1073
|
});
|
|
1074
1074
|
return {
|
|
1075
1075
|
defaultBranch,
|
|
@@ -1092,7 +1092,7 @@ function createEmptyTrendSeries() {
|
|
|
1092
1092
|
return Object.fromEntries(DASHBOARD_METRIC_KEYS.map((metric) => [metric, []]));
|
|
1093
1093
|
}
|
|
1094
1094
|
function normalizeEvents(events, pointLimit) {
|
|
1095
|
-
return events.filter((event) => Number.isFinite(event.ts) && event.ts > 0 && Number.isFinite(event.value)).sort((a, b) => a.ts - b.ts).slice(-pointLimit);
|
|
1095
|
+
return events.filter((event) => Number.isFinite(event.ts) && event.ts > 0 && Number.isFinite(event.value)).sort((a$1, b) => a$1.ts - b.ts).slice(-pointLimit);
|
|
1096
1096
|
}
|
|
1097
1097
|
function buildTimeWindow(options) {
|
|
1098
1098
|
const { probeEvents, targetBars, rightEdgeTs } = options;
|
|
@@ -1615,9 +1615,6 @@ function normalizeHuggingFaceEndpoint(endpoint) {
|
|
|
1615
1615
|
function buildHuggingFaceApiBaseUrl(endpoint) {
|
|
1616
1616
|
return `${normalizeHuggingFaceEndpoint(endpoint)}/api`;
|
|
1617
1617
|
}
|
|
1618
|
-
function buildTransformersRemoteHost(endpoint) {
|
|
1619
|
-
return `${normalizeHuggingFaceEndpoint(endpoint)}/`;
|
|
1620
|
-
}
|
|
1621
1618
|
|
|
1622
1619
|
//#endregion
|
|
1623
1620
|
//#region src/local-model-asset-store.ts
|
|
@@ -1686,9 +1683,21 @@ function getDefaultLocalModelCacheDir() {
|
|
|
1686
1683
|
function getDefaultLocalModelIndexPath() {
|
|
1687
1684
|
return join(getDefaultLocalModelCacheRoot(), "models.json");
|
|
1688
1685
|
}
|
|
1686
|
+
function getDefaultLocalModelProfileManifestPath() {
|
|
1687
|
+
return join(getDefaultLocalModelCacheRoot(), "profile-manifests.json");
|
|
1688
|
+
}
|
|
1689
1689
|
function getDefaultLocalModelFetchCachePath() {
|
|
1690
1690
|
return join(getDefaultLocalModelCacheRoot(), "fetch-cache.json");
|
|
1691
1691
|
}
|
|
1692
|
+
function getLocalModelProfileRoot(cacheDir, modelId) {
|
|
1693
|
+
return join(cacheDir, "profiles", sanitizeLocalModelPathSegment(modelId));
|
|
1694
|
+
}
|
|
1695
|
+
function getLocalModelProfileGroupRoot(cacheDir, modelId, groupId) {
|
|
1696
|
+
return join(getLocalModelProfileRoot(cacheDir, modelId), sanitizeLocalModelPathSegment(groupId));
|
|
1697
|
+
}
|
|
1698
|
+
function sanitizeLocalModelPathSegment(value) {
|
|
1699
|
+
return value.replace(/[^a-zA-Z0-9._-]+/g, "-");
|
|
1700
|
+
}
|
|
1692
1701
|
|
|
1693
1702
|
//#endregion
|
|
1694
1703
|
//#region src/local-model-fetch-cache-store.ts
|
|
@@ -1841,60 +1850,80 @@ function getTransformersLocalModelPath(cacheDir, modelId) {
|
|
|
1841
1850
|
function getTransformersFileCacheModelPath(cacheDir, modelId) {
|
|
1842
1851
|
return join(cacheDir, modelId);
|
|
1843
1852
|
}
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1853
|
+
|
|
1854
|
+
//#endregion
|
|
1855
|
+
//#region src/local-model-profile-manifest-store.ts
|
|
1856
|
+
const LocalModelProfileManifestIndexSchema = z.object({
|
|
1857
|
+
version: z.literal(1).default(1),
|
|
1858
|
+
manifests: z.array(LocalModelProfileManifestSchema).default([])
|
|
1859
|
+
});
|
|
1860
|
+
var LocalModelProfileManifestStore = class {
|
|
1861
|
+
constructor(options) {
|
|
1862
|
+
this.options = options;
|
|
1863
|
+
}
|
|
1864
|
+
getManifestPath() {
|
|
1865
|
+
return this.options.manifestPath;
|
|
1866
|
+
}
|
|
1867
|
+
async readAll() {
|
|
1868
|
+
return (await this.readFile()).manifests;
|
|
1869
|
+
}
|
|
1870
|
+
async readMap() {
|
|
1871
|
+
return new Map((await this.readAll()).map((manifest) => [manifest.modelId, manifest]));
|
|
1872
|
+
}
|
|
1873
|
+
async read(modelId) {
|
|
1874
|
+
return (await this.readMap()).get(modelId) ?? null;
|
|
1875
|
+
}
|
|
1876
|
+
async writeAll(manifests) {
|
|
1877
|
+
const normalized = LocalModelProfileManifestIndexSchema.parse({
|
|
1878
|
+
version: 1,
|
|
1879
|
+
manifests: [...manifests].sort((left, right) => left.modelId.localeCompare(right.modelId))
|
|
1880
|
+
});
|
|
1881
|
+
const serialized = JSON.stringify(normalized, null, 2);
|
|
1882
|
+
await mkdir(dirname(this.options.manifestPath), { recursive: true });
|
|
1883
|
+
const tempPath = `${this.options.manifestPath}.${process.pid}.${Date.now()}.tmp`;
|
|
1884
|
+
await writeFile(tempPath, `${serialized}\n`, "utf8");
|
|
1885
|
+
await rename(tempPath, this.options.manifestPath);
|
|
1886
|
+
clearCache();
|
|
1887
|
+
}
|
|
1888
|
+
async upsert(manifest) {
|
|
1889
|
+
const manifests = await this.readMap();
|
|
1890
|
+
manifests.set(manifest.modelId, LocalModelProfileManifestSchema.parse(manifest));
|
|
1891
|
+
await this.writeAll([...manifests.values()]);
|
|
1892
|
+
}
|
|
1893
|
+
async remove(modelId) {
|
|
1894
|
+
const manifests = await this.readMap();
|
|
1895
|
+
if (!manifests.delete(modelId)) return;
|
|
1896
|
+
await this.writeAll([...manifests.values()]);
|
|
1897
|
+
}
|
|
1898
|
+
async readFile() {
|
|
1899
|
+
try {
|
|
1900
|
+
const content = await readFile(this.options.manifestPath, "utf8");
|
|
1901
|
+
const parsed = JSON.parse(content);
|
|
1902
|
+
const result = LocalModelProfileManifestIndexSchema.safeParse(parsed);
|
|
1903
|
+
return result.success ? result.data : LocalModelProfileManifestIndexSchema.parse({});
|
|
1904
|
+
} catch {
|
|
1905
|
+
return LocalModelProfileManifestIndexSchema.parse({});
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
};
|
|
1857
1909
|
|
|
1858
1910
|
//#endregion
|
|
1859
1911
|
//#region src/local-model-runtime.ts
|
|
1860
|
-
async function
|
|
1861
|
-
|
|
1862
|
-
transformers.env.cacheDir = cacheDir;
|
|
1863
|
-
transformers.env.allowLocalModels = false;
|
|
1864
|
-
transformers.env.localModelPath = join(cacheDir, "models");
|
|
1865
|
-
}
|
|
1866
|
-
async function resolveLocalModelRuntimePlan(input) {
|
|
1867
|
-
await configureTransformersRuntime(input.transformers, input.cacheDir);
|
|
1868
|
-
const hubUrl = normalizeHuggingFaceEndpoint(input.hfEndpoint);
|
|
1869
|
-
const repositoryFiles = await readHuggingFaceRepositoryFiles({
|
|
1870
|
-
selectedGroupId: input.selectedGroupId,
|
|
1912
|
+
async function readLocalModelRepositorySnapshot(input) {
|
|
1913
|
+
return readHuggingFaceRepositorySnapshot({
|
|
1871
1914
|
modelId: input.modelId,
|
|
1872
|
-
hubUrl,
|
|
1873
|
-
fetchCacheStore: input.fetchCacheStore
|
|
1874
|
-
|
|
1875
|
-
return buildLocalDownloadPlanFromRepositoryFiles({
|
|
1876
|
-
modelId: input.modelId,
|
|
1877
|
-
selectedGroupId: input.selectedGroupId,
|
|
1878
|
-
files: repositoryFiles
|
|
1915
|
+
hubUrl: normalizeHuggingFaceEndpoint(input.hfEndpoint),
|
|
1916
|
+
fetchCacheStore: input.fetchCacheStore,
|
|
1917
|
+
revision: input.revision
|
|
1879
1918
|
});
|
|
1880
1919
|
}
|
|
1881
|
-
async function
|
|
1882
|
-
const
|
|
1883
|
-
const settings = await input.globalSettingsManager.readSettings();
|
|
1884
|
-
return resolveLocalModelRuntimePlan({
|
|
1885
|
-
modelId: input.modelId,
|
|
1886
|
-
transformers,
|
|
1887
|
-
cacheDir: input.cacheDir ?? getDefaultLocalModelCacheDir(),
|
|
1888
|
-
selectedGroupId: input.selectedGroupId,
|
|
1889
|
-
hfEndpoint: settings.translationEngines.local?.hfEndpoint,
|
|
1890
|
-
fetchCacheStore: input.fetchCacheStore
|
|
1891
|
-
});
|
|
1892
|
-
}
|
|
1893
|
-
async function readHuggingFaceRepositoryFiles(input) {
|
|
1920
|
+
async function readHuggingFaceRepositorySnapshot(input) {
|
|
1921
|
+
const detail = await readHuggingFaceModelSnapshotInfo(input).catch(() => null);
|
|
1894
1922
|
let lastError;
|
|
1895
1923
|
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
1896
1924
|
try {
|
|
1897
1925
|
const files = [];
|
|
1926
|
+
let commitHash = detail?.commitHash;
|
|
1898
1927
|
for await (const entry of listFiles({
|
|
1899
1928
|
repo: {
|
|
1900
1929
|
type: "model",
|
|
@@ -1902,16 +1931,30 @@ async function readHuggingFaceRepositoryFiles(input) {
|
|
|
1902
1931
|
},
|
|
1903
1932
|
recursive: true,
|
|
1904
1933
|
expand: true,
|
|
1934
|
+
revision: input.revision,
|
|
1905
1935
|
hubUrl: input.hubUrl,
|
|
1906
1936
|
fetch: input.fetchCacheStore ? createProviderFetchCache(input.fetchCacheStore) : void 0
|
|
1907
1937
|
})) {
|
|
1908
1938
|
if (entry.type !== "file") continue;
|
|
1939
|
+
commitHash ??= entry.lastCommit?.id;
|
|
1940
|
+
const etag = entry.lfs?.oid ?? entry.xetHash ?? entry.oid;
|
|
1909
1941
|
files.push({
|
|
1910
1942
|
path: entry.path,
|
|
1911
|
-
sizeBytes: entry.lfs?.size ?? entry.size
|
|
1943
|
+
sizeBytes: entry.lfs?.size ?? entry.size,
|
|
1944
|
+
etag,
|
|
1945
|
+
revision: entry.lastCommit?.id,
|
|
1946
|
+
sourceUrl: `${input.hubUrl}/${input.modelId}/resolve/${entry.lastCommit?.id ?? input.revision ?? "main"}/${entry.path}`,
|
|
1947
|
+
raw: entry
|
|
1912
1948
|
});
|
|
1913
1949
|
}
|
|
1914
|
-
if (files.length > 0) return
|
|
1950
|
+
if (files.length > 0 && commitHash) return {
|
|
1951
|
+
modelId: input.modelId,
|
|
1952
|
+
revision: input.revision ?? "main",
|
|
1953
|
+
commitHash,
|
|
1954
|
+
shortCommitHash: commitHash.slice(0, 6),
|
|
1955
|
+
files,
|
|
1956
|
+
raw: detail?.raw
|
|
1957
|
+
};
|
|
1915
1958
|
lastError = /* @__PURE__ */ new Error(`No repository files were returned for ${input.modelId}.`);
|
|
1916
1959
|
} catch (error) {
|
|
1917
1960
|
lastError = error;
|
|
@@ -1919,17 +1962,40 @@ async function readHuggingFaceRepositoryFiles(input) {
|
|
|
1919
1962
|
if (attempt < 2) await delay$2(300 * (attempt + 1));
|
|
1920
1963
|
}
|
|
1921
1964
|
const cachedFiles = await readCachedHuggingFaceRepositoryFiles(input);
|
|
1922
|
-
if (cachedFiles.length > 0) return
|
|
1965
|
+
if (cachedFiles.files.length > 0 && cachedFiles.commitHash) return {
|
|
1966
|
+
modelId: input.modelId,
|
|
1967
|
+
revision: input.revision ?? "main",
|
|
1968
|
+
commitHash: cachedFiles.commitHash,
|
|
1969
|
+
shortCommitHash: cachedFiles.commitHash.slice(0, 6),
|
|
1970
|
+
files: cachedFiles.files,
|
|
1971
|
+
raw: cachedFiles.raw
|
|
1972
|
+
};
|
|
1923
1973
|
throw lastError instanceof Error ? lastError : /* @__PURE__ */ new Error(`Unable to read repository files for ${input.modelId}.`);
|
|
1924
1974
|
}
|
|
1975
|
+
async function readHuggingFaceModelSnapshotInfo(input) {
|
|
1976
|
+
const detail = await modelInfo({
|
|
1977
|
+
name: input.modelId,
|
|
1978
|
+
hubUrl: input.hubUrl,
|
|
1979
|
+
revision: input.revision,
|
|
1980
|
+
additionalFields: ["sha"]
|
|
1981
|
+
});
|
|
1982
|
+
const raw = detail;
|
|
1983
|
+
const commitHash = typeof detail.sha === "string" ? detail.sha : void 0;
|
|
1984
|
+
if (!commitHash) throw new Error(`Unable to resolve a commit hash for ${input.modelId}.`);
|
|
1985
|
+
return {
|
|
1986
|
+
commitHash,
|
|
1987
|
+
raw
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1925
1990
|
async function readCachedHuggingFaceRepositoryFiles(input) {
|
|
1926
|
-
if (!input.fetchCacheStore) return [];
|
|
1991
|
+
if (!input.fetchCacheStore) return { files: [] };
|
|
1927
1992
|
const record = await input.fetchCacheStore.read(input.modelId);
|
|
1928
|
-
if (!record?.detailRaw) return [];
|
|
1993
|
+
if (!record?.detailRaw) return { files: [] };
|
|
1929
1994
|
return extractRepositoryFilesFromCachedDetail(record.detailRaw);
|
|
1930
1995
|
}
|
|
1931
1996
|
function extractRepositoryFilesFromCachedDetail(raw) {
|
|
1932
1997
|
const siblings = Array.isArray(raw.siblings) ? raw.siblings : [];
|
|
1998
|
+
const commitHash = typeof raw.sha === "string" ? raw.sha : void 0;
|
|
1933
1999
|
const files = [];
|
|
1934
2000
|
for (const sibling of siblings) {
|
|
1935
2001
|
if (!sibling || typeof sibling !== "object") continue;
|
|
@@ -1938,10 +2004,16 @@ function extractRepositoryFilesFromCachedDetail(raw) {
|
|
|
1938
2004
|
if (!path) continue;
|
|
1939
2005
|
files.push({
|
|
1940
2006
|
path,
|
|
1941
|
-
sizeBytes: typeof record.size === "number" && Number.isFinite(record.size) ? record.size : void 0
|
|
2007
|
+
sizeBytes: typeof record.size === "number" && Number.isFinite(record.size) ? record.size : void 0,
|
|
2008
|
+
revision: commitHash,
|
|
2009
|
+
raw: record
|
|
1942
2010
|
});
|
|
1943
2011
|
}
|
|
1944
|
-
return
|
|
2012
|
+
return {
|
|
2013
|
+
files,
|
|
2014
|
+
commitHash,
|
|
2015
|
+
raw
|
|
2016
|
+
};
|
|
1945
2017
|
}
|
|
1946
2018
|
function createProviderFetchCache(fetchCacheStore) {
|
|
1947
2019
|
return async (input, init) => {
|
|
@@ -1969,9 +2041,6 @@ function headersToRecord$1(headers) {
|
|
|
1969
2041
|
function delay$2(ms) {
|
|
1970
2042
|
return new Promise((resolve$1) => setTimeout(resolve$1, ms));
|
|
1971
2043
|
}
|
|
1972
|
-
async function loadLocalTransformersModule(_projectDir, _globalSettingsManager) {
|
|
1973
|
-
return await import("@huggingface/transformers");
|
|
1974
|
-
}
|
|
1975
2044
|
|
|
1976
2045
|
//#endregion
|
|
1977
2046
|
//#region src/network-dispatcher.ts
|
|
@@ -2421,6 +2490,7 @@ const DEFAULT_NETWORK_RETRY_DELAY_MAX_MS = 5e3;
|
|
|
2421
2490
|
var LocalModelAssetService = class {
|
|
2422
2491
|
now;
|
|
2423
2492
|
store;
|
|
2493
|
+
profileManifestStore;
|
|
2424
2494
|
cacheDir;
|
|
2425
2495
|
fetchCacheStore;
|
|
2426
2496
|
networkRetryPolicy;
|
|
@@ -2440,6 +2510,7 @@ var LocalModelAssetService = class {
|
|
|
2440
2510
|
maxDelayMs: options.networkRetryPolicy?.maxDelayMs ?? DEFAULT_NETWORK_RETRY_DELAY_MAX_MS
|
|
2441
2511
|
};
|
|
2442
2512
|
this.store = new LocalModelAssetStore({ indexPath: options.indexPath ?? getDefaultLocalModelIndexPath() });
|
|
2513
|
+
this.profileManifestStore = new LocalModelProfileManifestStore({ manifestPath: options.profileManifestPath ?? getDefaultLocalModelProfileManifestPath() });
|
|
2443
2514
|
this.fetchCacheStore = new LocalModelFetchCacheStore({
|
|
2444
2515
|
cachePath: options.fetchCachePath ?? getDefaultLocalModelFetchCachePath(),
|
|
2445
2516
|
now: this.now
|
|
@@ -2541,106 +2612,129 @@ var LocalModelAssetService = class {
|
|
|
2541
2612
|
async readSelectedModelState(modelId, selectedGroupId) {
|
|
2542
2613
|
const state = (await this.store.readMap()).get(modelId);
|
|
2543
2614
|
if (state) return this.refreshCachedState(state, selectedGroupId);
|
|
2544
|
-
const
|
|
2545
|
-
|
|
2546
|
-
const selected = modelId === await this.readSelectedModel();
|
|
2547
|
-
const plan = await this.readPlanForState(modelId, selectedGroupId ?? session.selectedGroupId);
|
|
2548
|
-
const selectedGroup = selectLocalDownloadGroup(plan, selectedGroupId ?? session.selectedGroupId);
|
|
2549
|
-
const files = (selectedGroup?.files ?? plan?.files ?? []).map((file) => ({
|
|
2550
|
-
path: file.path,
|
|
2551
|
-
sizeBytes: file.sizeBytes,
|
|
2552
|
-
downloadedBytes: 0
|
|
2553
|
-
}));
|
|
2554
|
-
return LocalModelAssetStateSchema.parse({
|
|
2555
|
-
modelId,
|
|
2556
|
-
plan: plan ?? void 0,
|
|
2557
|
-
status: "downloading",
|
|
2558
|
-
selected,
|
|
2559
|
-
resumable: true,
|
|
2560
|
-
totalBytes: selectedGroup?.estimatedTotalBytes ?? plan?.estimatedTotalBytes,
|
|
2561
|
-
progress: 0,
|
|
2562
|
-
files,
|
|
2563
|
-
updatedAt: this.now()
|
|
2564
|
-
});
|
|
2565
|
-
}
|
|
2566
|
-
return LocalModelAssetStateSchema.parse({
|
|
2615
|
+
const selected = modelId === await this.readSelectedModel();
|
|
2616
|
+
const baseState = LocalModelAssetStateSchema.parse({
|
|
2567
2617
|
modelId,
|
|
2568
2618
|
status: "not-downloaded",
|
|
2569
|
-
selected
|
|
2619
|
+
selected,
|
|
2620
|
+
selectedGroupId,
|
|
2570
2621
|
updatedAt: this.now()
|
|
2571
2622
|
});
|
|
2623
|
+
return this.refreshCachedState(baseState, selectedGroupId);
|
|
2572
2624
|
}
|
|
2573
|
-
async startDownload(modelId,
|
|
2574
|
-
return this.runDownload(modelId, "downloading", "Downloading local model",
|
|
2625
|
+
async startDownload(modelId, groupId) {
|
|
2626
|
+
return this.runDownload(modelId, "downloading", "Downloading local model", groupId);
|
|
2575
2627
|
}
|
|
2576
|
-
async resumeDownload(modelId,
|
|
2577
|
-
return this.runDownload(modelId, "downloading", "Resuming local model download",
|
|
2628
|
+
async resumeDownload(modelId, groupId) {
|
|
2629
|
+
return this.runDownload(modelId, "downloading", "Resuming local model download", groupId);
|
|
2578
2630
|
}
|
|
2579
|
-
async pauseDownload(modelId) {
|
|
2580
|
-
const
|
|
2631
|
+
async pauseDownload(modelId, groupId) {
|
|
2632
|
+
const requestedGroupId = groupId ?? await this.readSelectedGroupId();
|
|
2633
|
+
if (!requestedGroupId) return { success: true };
|
|
2634
|
+
const current = await this.readSelectedModelState(modelId, requestedGroupId);
|
|
2635
|
+
const effectiveGroupId = current.plan?.selectedGroupId ?? current.selectedGroupId ?? requestedGroupId;
|
|
2636
|
+
const sessionKey = buildSessionKey(modelId, effectiveGroupId);
|
|
2637
|
+
const session = this.sessions.get(sessionKey);
|
|
2581
2638
|
if (session) {
|
|
2582
2639
|
session.abortController.abort();
|
|
2583
|
-
this.sessions.delete(
|
|
2640
|
+
this.sessions.delete(sessionKey);
|
|
2584
2641
|
}
|
|
2585
|
-
const
|
|
2642
|
+
const nextGroupsState = {
|
|
2643
|
+
...current.groupsState,
|
|
2644
|
+
[effectiveGroupId]: LocalModelLifecycleGroupStateSchema.parse({
|
|
2645
|
+
...current.groupsState[effectiveGroupId],
|
|
2646
|
+
groupId: effectiveGroupId,
|
|
2647
|
+
status: "paused",
|
|
2648
|
+
resumable: true,
|
|
2649
|
+
updatedAt: this.now()
|
|
2650
|
+
})
|
|
2651
|
+
};
|
|
2586
2652
|
const nextState = LocalModelAssetStateSchema.parse({
|
|
2587
2653
|
...current,
|
|
2588
|
-
|
|
2589
|
-
resumable: true,
|
|
2654
|
+
groupsState: nextGroupsState,
|
|
2590
2655
|
updatedAt: this.now()
|
|
2591
2656
|
});
|
|
2592
|
-
await this.
|
|
2657
|
+
const projected = await this.refreshCachedState(nextState, effectiveGroupId, { revalidateDisk: true });
|
|
2658
|
+
await this.store.upsert(projected);
|
|
2593
2659
|
this.emitLog({
|
|
2594
2660
|
engineId: "local",
|
|
2595
2661
|
modelId,
|
|
2596
|
-
selectedGroupId:
|
|
2662
|
+
selectedGroupId: effectiveGroupId,
|
|
2663
|
+
groupId: effectiveGroupId,
|
|
2597
2664
|
status: "paused",
|
|
2598
2665
|
message: "Local model download paused.",
|
|
2599
|
-
progress:
|
|
2600
|
-
bytesDownloaded:
|
|
2601
|
-
totalBytes:
|
|
2666
|
+
progress: projected.progress,
|
|
2667
|
+
bytesDownloaded: projected.bytesDownloaded,
|
|
2668
|
+
totalBytes: projected.totalBytes,
|
|
2602
2669
|
resumable: true,
|
|
2603
|
-
files:
|
|
2670
|
+
files: projected.files,
|
|
2604
2671
|
updatedAt: this.now()
|
|
2605
2672
|
});
|
|
2606
2673
|
return { success: true };
|
|
2607
2674
|
}
|
|
2608
|
-
async deleteModel(modelId) {
|
|
2609
|
-
this.
|
|
2610
|
-
|
|
2611
|
-
|
|
2675
|
+
async deleteModel(modelId, groupId) {
|
|
2676
|
+
const requestedGroupId = groupId ?? await this.readSelectedGroupId();
|
|
2677
|
+
if (!requestedGroupId) {
|
|
2678
|
+
await this.store.remove(modelId);
|
|
2679
|
+
await this.profileManifestStore.remove(modelId);
|
|
2680
|
+
return { success: true };
|
|
2681
|
+
}
|
|
2682
|
+
const current = await this.readSelectedModelState(modelId, requestedGroupId);
|
|
2683
|
+
const effectiveGroupId = current.plan?.selectedGroupId ?? current.selectedGroupId ?? requestedGroupId;
|
|
2684
|
+
const sessionKey = buildSessionKey(modelId, effectiveGroupId);
|
|
2685
|
+
this.sessions.get(sessionKey)?.abortController.abort();
|
|
2686
|
+
this.sessions.delete(sessionKey);
|
|
2612
2687
|
await this.store.upsert(LocalModelAssetStateSchema.parse({
|
|
2613
2688
|
...current,
|
|
2614
|
-
|
|
2689
|
+
groupsState: {
|
|
2690
|
+
...current.groupsState,
|
|
2691
|
+
[effectiveGroupId]: LocalModelLifecycleGroupStateSchema.parse({
|
|
2692
|
+
...current.groupsState[effectiveGroupId],
|
|
2693
|
+
groupId: effectiveGroupId,
|
|
2694
|
+
status: "deleting",
|
|
2695
|
+
updatedAt: this.now()
|
|
2696
|
+
})
|
|
2697
|
+
},
|
|
2615
2698
|
updatedAt: this.now()
|
|
2616
2699
|
}));
|
|
2617
2700
|
this.emitLog({
|
|
2618
2701
|
engineId: "local",
|
|
2619
2702
|
modelId,
|
|
2620
|
-
selectedGroupId:
|
|
2703
|
+
selectedGroupId: effectiveGroupId,
|
|
2704
|
+
groupId: effectiveGroupId,
|
|
2621
2705
|
status: "deleting",
|
|
2622
2706
|
message: "Deleting local model files.",
|
|
2623
2707
|
files: current.files,
|
|
2624
2708
|
updatedAt: this.now()
|
|
2625
2709
|
});
|
|
2626
|
-
await
|
|
2627
|
-
await rm(getTransformersLocalModelPath(this.cacheDir, modelId), {
|
|
2628
|
-
recursive: true,
|
|
2629
|
-
force: true
|
|
2630
|
-
});
|
|
2631
|
-
await rm(getTransformersFileCacheModelPath(this.cacheDir, modelId), {
|
|
2710
|
+
await rm(getLocalModelProfileGroupRoot(this.cacheDir, modelId, effectiveGroupId), {
|
|
2632
2711
|
recursive: true,
|
|
2633
2712
|
force: true
|
|
2634
2713
|
});
|
|
2635
|
-
await
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2714
|
+
const persistedManifest = await this.profileManifestStore.read(modelId);
|
|
2715
|
+
const nextGroupsState = { ...current.groupsState };
|
|
2716
|
+
delete nextGroupsState[effectiveGroupId];
|
|
2717
|
+
const isPersistedManifestGroup = Boolean(persistedManifest?.groups[effectiveGroupId]);
|
|
2718
|
+
const nextManifest = isPersistedManifestGroup ? persistedManifest : current.profileManifest ? removeManifestGroup(current.profileManifest, effectiveGroupId) : void 0;
|
|
2719
|
+
const nextPlan = isPersistedManifestGroup ? void 0 : current.plan ? removePlanGroup(current.plan, effectiveGroupId) : void 0;
|
|
2720
|
+
const nextSelectedGroupId = current.selectedGroupId === effectiveGroupId ? void 0 : current.selectedGroupId;
|
|
2721
|
+
const nextState = await this.refreshCachedState(LocalModelAssetStateSchema.parse({
|
|
2722
|
+
...current,
|
|
2723
|
+
selectedGroupId: nextSelectedGroupId,
|
|
2724
|
+
profileManifest: nextManifest,
|
|
2725
|
+
groupsState: nextGroupsState,
|
|
2726
|
+
plan: nextPlan,
|
|
2727
|
+
updatedAt: this.now()
|
|
2728
|
+
}), nextSelectedGroupId, { revalidateDisk: true });
|
|
2729
|
+
if (nextState.profileManifest) await this.profileManifestStore.upsert(nextState.profileManifest);
|
|
2730
|
+
else await this.profileManifestStore.remove(modelId);
|
|
2731
|
+
if (nextState.profileManifest || nextState.plan?.groups?.length) await this.store.upsert(nextState);
|
|
2732
|
+
else await this.store.remove(modelId);
|
|
2640
2733
|
this.emitLog({
|
|
2641
2734
|
engineId: "local",
|
|
2642
2735
|
modelId,
|
|
2643
|
-
selectedGroupId:
|
|
2736
|
+
selectedGroupId: effectiveGroupId,
|
|
2737
|
+
groupId: effectiveGroupId,
|
|
2644
2738
|
status: "not-downloaded",
|
|
2645
2739
|
message: "Local model files were removed.",
|
|
2646
2740
|
progress: 0,
|
|
@@ -2651,6 +2745,49 @@ var LocalModelAssetService = class {
|
|
|
2651
2745
|
});
|
|
2652
2746
|
return { success: true };
|
|
2653
2747
|
}
|
|
2748
|
+
async refreshProfiles(modelId) {
|
|
2749
|
+
const targetModelId = modelId ?? await this.readSelectedModel();
|
|
2750
|
+
const loadingState = LocalModelAssetStateSchema.parse({
|
|
2751
|
+
...await this.readSelectedModelState(targetModelId),
|
|
2752
|
+
profileLoad: {
|
|
2753
|
+
status: "loading",
|
|
2754
|
+
message: "Loading local model profiles.",
|
|
2755
|
+
updatedAt: this.now()
|
|
2756
|
+
},
|
|
2757
|
+
updatedAt: this.now()
|
|
2758
|
+
});
|
|
2759
|
+
await this.store.upsert(loadingState);
|
|
2760
|
+
try {
|
|
2761
|
+
const manifest = await this.createProfileManifest(targetModelId);
|
|
2762
|
+
await this.profileManifestStore.upsert(manifest);
|
|
2763
|
+
const current = await this.readSelectedModelState(targetModelId);
|
|
2764
|
+
const nextState = await this.refreshCachedState(LocalModelAssetStateSchema.parse({
|
|
2765
|
+
...current,
|
|
2766
|
+
profileManifest: manifest,
|
|
2767
|
+
profileLoad: {
|
|
2768
|
+
status: "ready",
|
|
2769
|
+
message: "Local model profiles are ready.",
|
|
2770
|
+
updatedAt: this.now()
|
|
2771
|
+
},
|
|
2772
|
+
updatedAt: this.now()
|
|
2773
|
+
}), void 0, { revalidateDisk: true });
|
|
2774
|
+
await this.store.upsert(nextState);
|
|
2775
|
+
return nextState;
|
|
2776
|
+
} catch (error) {
|
|
2777
|
+
const message = error instanceof Error ? error.message : "Unable to load local model profiles.";
|
|
2778
|
+
const failedState = LocalModelAssetStateSchema.parse({
|
|
2779
|
+
...await this.readSelectedModelState(targetModelId),
|
|
2780
|
+
profileLoad: {
|
|
2781
|
+
status: "error",
|
|
2782
|
+
error: message,
|
|
2783
|
+
updatedAt: this.now()
|
|
2784
|
+
},
|
|
2785
|
+
updatedAt: this.now()
|
|
2786
|
+
});
|
|
2787
|
+
await this.store.upsert(failedState);
|
|
2788
|
+
throw error;
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2654
2791
|
async markSelectedModel(modelId) {
|
|
2655
2792
|
const nextStates = (await this.store.readAll()).map((state) => LocalModelAssetStateSchema.parse({
|
|
2656
2793
|
...state,
|
|
@@ -2665,7 +2802,7 @@ var LocalModelAssetService = class {
|
|
|
2665
2802
|
await this.store.writeAll(nextStates);
|
|
2666
2803
|
}
|
|
2667
2804
|
async waitForModelTask(modelId) {
|
|
2668
|
-
await this.sessionTasks.
|
|
2805
|
+
await Promise.all([...this.sessionTasks.entries()].filter(([sessionKey]) => sessionKey.startsWith(`${modelId}:`)).map(([, task]) => task));
|
|
2669
2806
|
}
|
|
2670
2807
|
async close() {
|
|
2671
2808
|
const sessions = [...this.sessions.values()];
|
|
@@ -2724,176 +2861,298 @@ var LocalModelAssetService = class {
|
|
|
2724
2861
|
}));
|
|
2725
2862
|
return [...remoteItems, ...localOnlyItems];
|
|
2726
2863
|
}
|
|
2727
|
-
async refreshCachedState(state, selectedGroupId) {
|
|
2728
|
-
const
|
|
2729
|
-
const
|
|
2730
|
-
const
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
sizeBytes: file.sizeBytes,
|
|
2740
|
-
downloadedBytes: file.sizeBytes
|
|
2741
|
-
}));
|
|
2742
|
-
return LocalModelAssetStateSchema.parse({
|
|
2743
|
-
...state,
|
|
2744
|
-
selected,
|
|
2745
|
-
status: "downloaded",
|
|
2746
|
-
progress: 1,
|
|
2747
|
-
bytesDownloaded: state.totalBytes ?? state.plan.estimatedTotalBytes,
|
|
2748
|
-
totalBytes: state.totalBytes ?? state.plan.estimatedTotalBytes,
|
|
2749
|
-
resumable: false,
|
|
2750
|
-
error: void 0,
|
|
2751
|
-
files: files$1,
|
|
2752
|
-
updatedAt: this.now(),
|
|
2753
|
-
installedAt: state.installedAt ?? this.now()
|
|
2754
|
-
});
|
|
2755
|
-
}
|
|
2756
|
-
const transformers = await this.getTransformersModule();
|
|
2757
|
-
transformers.env.remoteHost = buildTransformersRemoteHost(await this.readHuggingFaceEndpoint());
|
|
2758
|
-
const [plan, persistedSelectedGroupId] = await Promise.all([this.readPlan(state.modelId, transformers, requestedGroupId ?? state.plan?.selectedGroupId), this.readSelectedGroupId()]);
|
|
2759
|
-
if (!plan && state.status !== "downloaded") return LocalModelAssetStateSchema.parse({
|
|
2760
|
-
...state,
|
|
2761
|
-
selected,
|
|
2762
|
-
plan: void 0
|
|
2864
|
+
async refreshCachedState(state, selectedGroupId, options = {}) {
|
|
2865
|
+
const [selectedModel, persistedSelectedGroupId] = await Promise.all([this.readSelectedModel(), this.readSelectedGroupId()]);
|
|
2866
|
+
const selected = state.selected || state.modelId === selectedModel;
|
|
2867
|
+
const selectedGroupIdFromSettings = selectedGroupId ?? persistedSelectedGroupId;
|
|
2868
|
+
const manifest = filterConcreteProfileManifest(state.profileManifest ?? await this.profileManifestStore.read(state.modelId) ?? void 0);
|
|
2869
|
+
const migrated = migrateLegacyStateToGroups(state, manifest, this.now());
|
|
2870
|
+
const manifestWithHistoricalGroups = mergeHistoricalGroupsIntoManifest({
|
|
2871
|
+
cacheDir: this.cacheDir,
|
|
2872
|
+
modelId: state.modelId,
|
|
2873
|
+
manifest,
|
|
2874
|
+
groupsState: migrated.groupsState,
|
|
2875
|
+
fallbackPlan: migrated.plan
|
|
2763
2876
|
});
|
|
2764
|
-
const
|
|
2877
|
+
const selectedGroupIdForProjection = resolveManifestGroupId(manifestWithHistoricalGroups, selectedGroupIdFromSettings ?? migrated.selectedGroupId ?? migrated.plan?.selectedGroupId) ?? selectFirstManifestGroupId(manifestWithHistoricalGroups);
|
|
2878
|
+
const reconciledGroupsState = options.revalidateDisk ? await this.reconcileGroupsFromDisk({
|
|
2765
2879
|
modelId: state.modelId,
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
selectedGroupId: state.plan?.selectedGroupId,
|
|
2773
|
-
groups: state.plan?.groups
|
|
2774
|
-
};
|
|
2775
|
-
const selectedGroup = selectLocalDownloadGroup(effectivePlan, requestedGroupId ?? state.plan?.selectedGroupId ?? persistedSelectedGroupId);
|
|
2776
|
-
if (requestedGroupId && requestedGroupId !== effectivePlan.selectedGroupId && !selectedGroup) return LocalModelAssetStateSchema.parse({
|
|
2777
|
-
...state,
|
|
2778
|
-
selected,
|
|
2779
|
-
status: "not-downloaded",
|
|
2780
|
-
progress: 0,
|
|
2781
|
-
bytesDownloaded: 0,
|
|
2782
|
-
totalBytes: void 0,
|
|
2783
|
-
resumable: false,
|
|
2784
|
-
files: [],
|
|
2785
|
-
updatedAt: this.now()
|
|
2880
|
+
manifest: manifestWithHistoricalGroups,
|
|
2881
|
+
groupsState: migrated.groupsState
|
|
2882
|
+
}) : this.reconcileGroupsFromSnapshot({
|
|
2883
|
+
modelId: state.modelId,
|
|
2884
|
+
manifest: manifestWithHistoricalGroups,
|
|
2885
|
+
groupsState: migrated.groupsState
|
|
2786
2886
|
});
|
|
2787
|
-
const
|
|
2788
|
-
const cacheStatus = await readLocalModelFileStatus({
|
|
2789
|
-
cacheDir: this.cacheDir,
|
|
2887
|
+
const plan = buildPlanFromManifest({
|
|
2790
2888
|
modelId: state.modelId,
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
const
|
|
2796
|
-
const
|
|
2797
|
-
|
|
2798
|
-
const
|
|
2799
|
-
const downloadedBytes = file.sizeBytes === void 0 ? cached ? existingDownloadedBytes : existingDownloadedBytes : cached ? file.sizeBytes : Math.min(existingDownloadedBytes, file.sizeBytes);
|
|
2889
|
+
manifest: manifestWithHistoricalGroups,
|
|
2890
|
+
groupsState: reconciledGroupsState,
|
|
2891
|
+
selectedGroupId: selectedGroupIdForProjection
|
|
2892
|
+
});
|
|
2893
|
+
const selectedPlanGroup = selectLocalDownloadGroup(plan, selectedGroupIdForProjection);
|
|
2894
|
+
const selectedGroupState = selectedPlanGroup && reconciledGroupsState[selectedPlanGroup.id] ? reconciledGroupsState[selectedPlanGroup.id] : void 0;
|
|
2895
|
+
const files = selectedPlanGroup?.files.map((file) => {
|
|
2896
|
+
const stateFile = selectedGroupState?.files.find((entry) => entry.path === file.path);
|
|
2800
2897
|
return {
|
|
2801
2898
|
path: file.path,
|
|
2802
2899
|
sizeBytes: file.sizeBytes,
|
|
2803
|
-
downloadedBytes
|
|
2900
|
+
downloadedBytes: stateFile?.downloadedBytes ?? 0
|
|
2804
2901
|
};
|
|
2805
|
-
});
|
|
2806
|
-
const
|
|
2807
|
-
const detectedProgress = effectivePlan.estimatedTotalBytes !== void 0 && effectivePlan.estimatedTotalBytes > 0 ? detectedBytesDownloaded / effectivePlan.estimatedTotalBytes : runtimeAllCached ? 1 : void 0;
|
|
2808
|
-
const progress = cacheStatus.allCached ? 1 : session ? state.progress : detectedProgress;
|
|
2809
|
-
const hasPartialCache = !runtimeAllCached && detectedBytesDownloaded > 0;
|
|
2902
|
+
}) ?? [];
|
|
2903
|
+
const status = selectedGroupState?.status ?? "not-downloaded";
|
|
2810
2904
|
return LocalModelAssetStateSchema.parse({
|
|
2811
|
-
...
|
|
2905
|
+
...migrated,
|
|
2812
2906
|
selected,
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2907
|
+
selectedGroupId: selectedGroupIdForProjection,
|
|
2908
|
+
profileManifest: manifestWithHistoricalGroups,
|
|
2909
|
+
groupsState: reconciledGroupsState,
|
|
2910
|
+
plan: plan ?? void 0,
|
|
2911
|
+
status,
|
|
2912
|
+
progress: selectedGroupState?.progress,
|
|
2913
|
+
totalBytes: selectedGroupState?.totalBytes ?? selectedPlanGroup?.estimatedTotalBytes,
|
|
2914
|
+
bytesDownloaded: selectedGroupState?.bytesDownloaded,
|
|
2915
|
+
error: selectedGroupState?.error,
|
|
2916
|
+
resumable: selectedGroupState?.resumable ?? false,
|
|
2820
2917
|
files,
|
|
2821
2918
|
updatedAt: this.now(),
|
|
2822
|
-
installedAt:
|
|
2919
|
+
installedAt: selectedGroupState?.installedAt ?? state.installedAt
|
|
2823
2920
|
});
|
|
2824
2921
|
}
|
|
2825
|
-
|
|
2826
|
-
|
|
2922
|
+
reconcileGroupsFromSnapshot(input) {
|
|
2923
|
+
if (!input.manifest) return input.groupsState;
|
|
2924
|
+
const nextGroupsState = { ...input.groupsState };
|
|
2925
|
+
for (const groupId of input.manifest.groupOrder) {
|
|
2926
|
+
const manifestGroup = input.manifest.groups[groupId];
|
|
2927
|
+
if (!manifestGroup) continue;
|
|
2928
|
+
const current = nextGroupsState[groupId];
|
|
2929
|
+
const files = reconcileGroupFilesFromSnapshot({
|
|
2930
|
+
manifestGroup,
|
|
2931
|
+
currentFiles: current?.files ?? [],
|
|
2932
|
+
currentStatus: current?.status ?? "not-downloaded"
|
|
2933
|
+
});
|
|
2934
|
+
const bytesDownloaded = sumDownloadedBytes(files);
|
|
2935
|
+
const totalBytes = manifestGroup.estimatedTotalBytes;
|
|
2936
|
+
const status = current?.status ?? "not-downloaded";
|
|
2937
|
+
nextGroupsState[groupId] = LocalModelLifecycleGroupStateSchema.parse({
|
|
2938
|
+
...current,
|
|
2939
|
+
groupId,
|
|
2940
|
+
baseGroupId: manifestGroup.baseGroupId,
|
|
2941
|
+
status,
|
|
2942
|
+
rootDir: manifestGroup.rootDir,
|
|
2943
|
+
bytesDownloaded,
|
|
2944
|
+
totalBytes,
|
|
2945
|
+
progress: totalBytes && totalBytes > 0 ? Math.max(0, Math.min(1, bytesDownloaded / totalBytes)) : current?.progress,
|
|
2946
|
+
resumable: current?.resumable ?? (status === "paused" || status === "error" || status === "downloading"),
|
|
2947
|
+
error: current?.error,
|
|
2948
|
+
installedAt: current?.installedAt,
|
|
2949
|
+
updatedAt: current?.updatedAt ?? this.now(),
|
|
2950
|
+
files
|
|
2951
|
+
});
|
|
2952
|
+
}
|
|
2953
|
+
return nextGroupsState;
|
|
2954
|
+
}
|
|
2955
|
+
async reconcileGroupsFromDisk(input) {
|
|
2956
|
+
if (!input.manifest) return input.groupsState;
|
|
2957
|
+
const nextGroupsState = { ...input.groupsState };
|
|
2958
|
+
for (const groupId of input.manifest.groupOrder) {
|
|
2959
|
+
const manifestGroup = input.manifest.groups[groupId];
|
|
2960
|
+
if (!manifestGroup) continue;
|
|
2961
|
+
const current = nextGroupsState[groupId];
|
|
2962
|
+
if (isActiveDownloadStatus(current?.status ?? "not-downloaded")) {
|
|
2963
|
+
nextGroupsState[groupId] = LocalModelLifecycleGroupStateSchema.parse({
|
|
2964
|
+
...current,
|
|
2965
|
+
groupId,
|
|
2966
|
+
baseGroupId: manifestGroup.baseGroupId,
|
|
2967
|
+
rootDir: manifestGroup.rootDir,
|
|
2968
|
+
totalBytes: manifestGroup.estimatedTotalBytes,
|
|
2969
|
+
files: reconcileGroupFiles({
|
|
2970
|
+
manifestGroup,
|
|
2971
|
+
currentFiles: current?.files ?? []
|
|
2972
|
+
})
|
|
2973
|
+
});
|
|
2974
|
+
continue;
|
|
2975
|
+
}
|
|
2976
|
+
const files = await reconcileGroupFilesFromDisk({
|
|
2977
|
+
rootDir: manifestGroup.rootDir,
|
|
2978
|
+
manifestGroup,
|
|
2979
|
+
currentFiles: current?.files ?? []
|
|
2980
|
+
});
|
|
2981
|
+
const bytesDownloaded = sumDownloadedBytes(files);
|
|
2982
|
+
const totalBytes = manifestGroup.estimatedTotalBytes;
|
|
2983
|
+
const allComplete = files.length > 0 && files.every((file) => file.sizeBytes !== void 0 && (file.downloadedBytes ?? 0) >= file.sizeBytes && file.status === "downloaded");
|
|
2984
|
+
const hasPartial = files.some((file) => (file.downloadedBytes ?? 0) > 0);
|
|
2985
|
+
const status = allComplete ? "downloaded" : current?.status === "error" ? "error" : current?.status === "paused" ? "paused" : hasPartial ? "paused" : "not-downloaded";
|
|
2986
|
+
nextGroupsState[groupId] = LocalModelLifecycleGroupStateSchema.parse({
|
|
2987
|
+
...current,
|
|
2988
|
+
groupId,
|
|
2989
|
+
baseGroupId: manifestGroup.baseGroupId,
|
|
2990
|
+
status,
|
|
2991
|
+
rootDir: manifestGroup.rootDir,
|
|
2992
|
+
bytesDownloaded,
|
|
2993
|
+
totalBytes,
|
|
2994
|
+
progress: totalBytes && totalBytes > 0 ? Math.max(0, Math.min(1, bytesDownloaded / totalBytes)) : void 0,
|
|
2995
|
+
resumable: status === "paused" || status === "error",
|
|
2996
|
+
error: status === "error" ? current?.error : void 0,
|
|
2997
|
+
installedAt: status === "downloaded" ? current?.installedAt ?? this.now() : current?.installedAt,
|
|
2998
|
+
updatedAt: this.now(),
|
|
2999
|
+
files
|
|
3000
|
+
});
|
|
3001
|
+
}
|
|
3002
|
+
return nextGroupsState;
|
|
3003
|
+
}
|
|
3004
|
+
async runDownload(modelId, targetStatus, messagePrefix, groupId) {
|
|
3005
|
+
const effectiveGroupId = groupId ?? await this.readSelectedGroupId();
|
|
3006
|
+
if (!effectiveGroupId) throw new Error("No local model profile is selected.");
|
|
3007
|
+
const manifest = await this.ensureProfileManifest(modelId);
|
|
3008
|
+
const resolvedGroupId = resolveManifestGroupId(manifest, effectiveGroupId);
|
|
3009
|
+
if (!resolvedGroupId) throw new Error("No concrete local model download plan is available.");
|
|
3010
|
+
const sessionKey = buildSessionKey(modelId, resolvedGroupId);
|
|
3011
|
+
const existing = this.sessions.get(sessionKey);
|
|
2827
3012
|
if (existing) return { sessionId: existing.sessionId };
|
|
2828
|
-
const sessionId = `local-model-${sanitizeId(modelId)}-${this.now()}`;
|
|
3013
|
+
const sessionId = `local-model-${sanitizeId(modelId)}-${sanitizeId(resolvedGroupId)}-${this.now()}`;
|
|
2829
3014
|
const abortController = new AbortController();
|
|
2830
|
-
this.sessions.set(
|
|
3015
|
+
this.sessions.set(sessionKey, {
|
|
2831
3016
|
modelId,
|
|
2832
3017
|
sessionId,
|
|
2833
3018
|
abortController,
|
|
2834
|
-
|
|
3019
|
+
groupId: resolvedGroupId
|
|
2835
3020
|
});
|
|
2836
|
-
const current = await this.readSelectedModelState(modelId);
|
|
2837
|
-
const
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
this.sessions.delete(modelId);
|
|
3021
|
+
const current = await this.readSelectedModelState(modelId, resolvedGroupId);
|
|
3022
|
+
const manifestGroup = manifest.groups[resolvedGroupId];
|
|
3023
|
+
if (!manifestGroup || manifestGroup.files.length === 0 || manifestGroup.estimatedTotalBytes === void 0) {
|
|
3024
|
+
this.sessions.delete(sessionKey);
|
|
2841
3025
|
throw new Error("No concrete local model download plan is available.");
|
|
2842
3026
|
}
|
|
2843
|
-
const totalBytes =
|
|
2844
|
-
const
|
|
2845
|
-
|
|
2846
|
-
|
|
3027
|
+
const totalBytes = manifestGroup.estimatedTotalBytes;
|
|
3028
|
+
const currentGroup = current.groupsState[resolvedGroupId];
|
|
3029
|
+
const resumedFiles = await reconcileGroupFilesFromDisk({
|
|
3030
|
+
rootDir: manifestGroup.rootDir,
|
|
3031
|
+
manifestGroup,
|
|
3032
|
+
currentFiles: currentGroup?.files ?? []
|
|
2847
3033
|
});
|
|
2848
3034
|
const resumedBytesDownloaded = sumDownloadedBytes(resumedFiles);
|
|
2849
3035
|
const nextState = LocalModelAssetStateSchema.parse({
|
|
2850
3036
|
...current,
|
|
2851
3037
|
modelId,
|
|
2852
|
-
plan,
|
|
2853
|
-
status: targetStatus,
|
|
2854
3038
|
selected: true,
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
3039
|
+
profileManifest: manifest,
|
|
3040
|
+
groupsState: {
|
|
3041
|
+
...current.groupsState,
|
|
3042
|
+
[resolvedGroupId]: LocalModelLifecycleGroupStateSchema.parse({
|
|
3043
|
+
...currentGroup,
|
|
3044
|
+
groupId: resolvedGroupId,
|
|
3045
|
+
baseGroupId: manifestGroup.baseGroupId,
|
|
3046
|
+
status: targetStatus,
|
|
3047
|
+
rootDir: manifestGroup.rootDir,
|
|
3048
|
+
bytesDownloaded: resumedBytesDownloaded,
|
|
3049
|
+
progress: totalBytes > 0 ? resumedBytesDownloaded / totalBytes : currentGroup?.progress,
|
|
3050
|
+
totalBytes,
|
|
3051
|
+
resumable: true,
|
|
3052
|
+
files: resumedFiles,
|
|
3053
|
+
updatedAt: this.now()
|
|
3054
|
+
})
|
|
3055
|
+
},
|
|
2860
3056
|
updatedAt: this.now()
|
|
2861
3057
|
});
|
|
2862
|
-
await this.
|
|
3058
|
+
const projected = await this.refreshCachedState(nextState, resolvedGroupId, { revalidateDisk: true });
|
|
3059
|
+
await this.store.upsert(projected);
|
|
2863
3060
|
this.emitLog({
|
|
2864
3061
|
engineId: "local",
|
|
2865
3062
|
modelId,
|
|
2866
|
-
selectedGroupId:
|
|
3063
|
+
selectedGroupId: resolvedGroupId,
|
|
3064
|
+
groupId: resolvedGroupId,
|
|
2867
3065
|
status: targetStatus,
|
|
2868
3066
|
message: `${messagePrefix} ${modelId}.`,
|
|
2869
|
-
progress:
|
|
2870
|
-
bytesDownloaded:
|
|
3067
|
+
progress: projected.progress,
|
|
3068
|
+
bytesDownloaded: projected.bytesDownloaded,
|
|
2871
3069
|
totalBytes,
|
|
2872
3070
|
sessionId,
|
|
2873
3071
|
resumable: true,
|
|
2874
|
-
files:
|
|
3072
|
+
files: projected.files,
|
|
2875
3073
|
updatedAt: this.now()
|
|
2876
3074
|
});
|
|
2877
|
-
const task = this.performDownload(modelId, sessionId, abortController.signal
|
|
2878
|
-
if (this.sessionTasks.get(
|
|
3075
|
+
const task = this.performDownload(modelId, resolvedGroupId, sessionId, abortController.signal).catch((error) => this.finishDownload(modelId, resolvedGroupId, sessionId, false, error instanceof Error ? error.message : String(error))).finally(() => {
|
|
3076
|
+
if (this.sessionTasks.get(sessionKey) === task) this.sessionTasks.delete(sessionKey);
|
|
2879
3077
|
});
|
|
2880
|
-
this.sessionTasks.set(
|
|
3078
|
+
this.sessionTasks.set(sessionKey, task);
|
|
2881
3079
|
return { sessionId };
|
|
2882
3080
|
}
|
|
2883
|
-
async
|
|
2884
|
-
const
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
3081
|
+
async ensureProfileManifest(modelId) {
|
|
3082
|
+
const existing = await this.profileManifestStore.read(modelId);
|
|
3083
|
+
if (existing) return existing;
|
|
3084
|
+
const manifest = await this.createProfileManifest(modelId);
|
|
3085
|
+
await this.profileManifestStore.upsert(manifest);
|
|
3086
|
+
return manifest;
|
|
3087
|
+
}
|
|
3088
|
+
async createProfileManifest(modelId) {
|
|
3089
|
+
const hfEndpoint = await this.readHuggingFaceEndpoint();
|
|
3090
|
+
const snapshot = await readLocalModelRepositorySnapshot({
|
|
3091
|
+
modelId,
|
|
3092
|
+
hfEndpoint,
|
|
3093
|
+
fetchCacheStore: this.fetchCacheStore
|
|
3094
|
+
});
|
|
3095
|
+
const basePlan = buildLocalDownloadPlanFromRepositoryFiles({
|
|
3096
|
+
modelId,
|
|
3097
|
+
files: snapshot.files.map((file) => ({
|
|
3098
|
+
...file,
|
|
3099
|
+
revision: snapshot.commitHash
|
|
3100
|
+
}))
|
|
3101
|
+
});
|
|
3102
|
+
if (!basePlan?.groups?.length) throw new Error(`No recognizable local model profiles were found for ${modelId}.`);
|
|
3103
|
+
const groupsEntries = basePlan.groups.flatMap((group) => {
|
|
3104
|
+
if (!group.selectable || group.estimatedTotalBytes === void 0) return [];
|
|
3105
|
+
const groupId = buildVersionedGroupId(group.id, snapshot.shortCommitHash);
|
|
3106
|
+
const rootDir = getLocalModelProfileGroupRoot(this.cacheDir, modelId, groupId);
|
|
3107
|
+
return [[groupId, {
|
|
3108
|
+
id: groupId,
|
|
3109
|
+
baseGroupId: group.id,
|
|
3110
|
+
label: group.label,
|
|
3111
|
+
displayLabel: group.label,
|
|
3112
|
+
description: group.description,
|
|
3113
|
+
profile: group.profile,
|
|
3114
|
+
dtype: group.dtype,
|
|
3115
|
+
commitHash: snapshot.commitHash,
|
|
3116
|
+
shortCommitHash: snapshot.shortCommitHash,
|
|
3117
|
+
rootDir,
|
|
3118
|
+
estimatedTotalBytes: group.estimatedTotalBytes,
|
|
3119
|
+
selectable: group.selectable,
|
|
3120
|
+
files: group.files.map((file) => ({
|
|
3121
|
+
...file,
|
|
3122
|
+
revision: snapshot.commitHash,
|
|
3123
|
+
sourceUrl: file.sourceUrl ?? `${normalizeHuggingFaceEndpoint(hfEndpoint)}/${modelId}/resolve/${snapshot.commitHash}/${file.path}`
|
|
3124
|
+
}))
|
|
3125
|
+
}]];
|
|
3126
|
+
});
|
|
3127
|
+
if (groupsEntries.length === 0) throw new Error(`No selectable local model profiles were found for ${modelId}.`);
|
|
3128
|
+
return LocalModelProfileManifestSchema.parse({
|
|
3129
|
+
modelId,
|
|
3130
|
+
source: "huggingface",
|
|
3131
|
+
endpoint: normalizeHuggingFaceEndpoint(hfEndpoint),
|
|
3132
|
+
revision: snapshot.revision,
|
|
3133
|
+
commitHash: snapshot.commitHash,
|
|
3134
|
+
shortCommitHash: snapshot.shortCommitHash,
|
|
3135
|
+
fetchedAt: this.now(),
|
|
3136
|
+
updatedAt: this.now(),
|
|
3137
|
+
raw: snapshot.raw,
|
|
3138
|
+
groups: Object.fromEntries(groupsEntries),
|
|
3139
|
+
groupOrder: groupsEntries.map(([groupId]) => groupId)
|
|
3140
|
+
});
|
|
3141
|
+
}
|
|
3142
|
+
async performDownload(modelId, groupId, sessionId, signal) {
|
|
3143
|
+
const manifestGroup = (await this.ensureProfileManifest(modelId)).groups[groupId];
|
|
3144
|
+
if (!manifestGroup) throw new Error(`Unknown local model profile: ${groupId}.`);
|
|
3145
|
+
const files = manifestGroup.files;
|
|
3146
|
+
const totalBytes = manifestGroup.estimatedTotalBytes;
|
|
2890
3147
|
const hfEndpoint = normalizeHuggingFaceEndpoint(await this.readHuggingFaceEndpoint());
|
|
2891
|
-
const
|
|
2892
|
-
|
|
2893
|
-
|
|
3148
|
+
const currentGroup = (await this.readSelectedModelState(modelId, groupId)).groupsState[groupId];
|
|
3149
|
+
const downloadedFiles = await reconcileGroupFilesFromDisk({
|
|
3150
|
+
rootDir: manifestGroup.rootDir,
|
|
3151
|
+
manifestGroup,
|
|
3152
|
+
currentFiles: currentGroup?.files ?? []
|
|
2894
3153
|
});
|
|
2895
3154
|
let bytesDownloaded = sumDownloadedBytes(downloadedFiles);
|
|
2896
|
-
if (files.length === 0) throw new Error("No concrete local model download files were selected.");
|
|
3155
|
+
if (files.length === 0 || totalBytes === void 0) throw new Error("No concrete local model download files were selected.");
|
|
2897
3156
|
for (const [fileIndex, file] of files.entries()) {
|
|
2898
3157
|
throwIfAborted(signal);
|
|
2899
3158
|
const previousFileBytes = downloadedFiles[fileIndex]?.downloadedBytes ?? 0;
|
|
@@ -2901,12 +3160,14 @@ var LocalModelAssetService = class {
|
|
|
2901
3160
|
downloadedFiles[fileIndex] = {
|
|
2902
3161
|
path: file.path,
|
|
2903
3162
|
sizeBytes: file.sizeBytes,
|
|
2904
|
-
downloadedBytes: previousFileBytes
|
|
3163
|
+
downloadedBytes: previousFileBytes,
|
|
3164
|
+
required: file.required,
|
|
3165
|
+
status: previousFileBytes > 0 ? "paused" : "not-downloaded"
|
|
2905
3166
|
};
|
|
2906
3167
|
await this.emitDownloadProgress({
|
|
2907
3168
|
modelId,
|
|
3169
|
+
groupId,
|
|
2908
3170
|
sessionId,
|
|
2909
|
-
state,
|
|
2910
3171
|
message: `Downloading ${file.path}.`,
|
|
2911
3172
|
totalBytes,
|
|
2912
3173
|
bytesDownloaded,
|
|
@@ -2919,7 +3180,10 @@ var LocalModelAssetService = class {
|
|
|
2919
3180
|
},
|
|
2920
3181
|
path: file.path,
|
|
2921
3182
|
cacheDir: this.cacheDir,
|
|
3183
|
+
targetPath: join(manifestGroup.rootDir, file.path),
|
|
2922
3184
|
hubUrl: hfEndpoint,
|
|
3185
|
+
revision: manifestGroup.commitHash,
|
|
3186
|
+
etag: file.etag,
|
|
2923
3187
|
expectedSizeBytes: file.sizeBytes,
|
|
2924
3188
|
retryPolicy: this.networkRetryPolicy,
|
|
2925
3189
|
fetch: createAbortableFetch(signal),
|
|
@@ -2930,12 +3194,14 @@ var LocalModelAssetService = class {
|
|
|
2930
3194
|
downloadedFiles[fileIndex] = {
|
|
2931
3195
|
path: file.path,
|
|
2932
3196
|
sizeBytes: file.sizeBytes,
|
|
2933
|
-
downloadedBytes: boundedFileBytes
|
|
3197
|
+
downloadedBytes: boundedFileBytes,
|
|
3198
|
+
required: file.required,
|
|
3199
|
+
status: boundedFileBytes >= (file.sizeBytes ?? Number.POSITIVE_INFINITY) ? "downloaded" : "downloading"
|
|
2934
3200
|
};
|
|
2935
3201
|
await this.emitDownloadProgress({
|
|
2936
3202
|
modelId,
|
|
3203
|
+
groupId,
|
|
2937
3204
|
sessionId,
|
|
2938
|
-
state,
|
|
2939
3205
|
message: `Downloading ${file.path}.`,
|
|
2940
3206
|
totalBytes,
|
|
2941
3207
|
bytesDownloaded: bytesDownloaded - previousFileBytes + boundedFileBytes,
|
|
@@ -2946,8 +3212,8 @@ var LocalModelAssetService = class {
|
|
|
2946
3212
|
const retryTarget = phase === "metadata" ? `metadata for ${file.path}` : `${file.path}`;
|
|
2947
3213
|
await this.emitDownloadProgress({
|
|
2948
3214
|
modelId,
|
|
3215
|
+
groupId,
|
|
2949
3216
|
sessionId,
|
|
2950
|
-
state,
|
|
2951
3217
|
message: `Connection interrupted while downloading ${retryTarget}. Retrying automatically in ${formatDuration(retryDelayMs)}.`,
|
|
2952
3218
|
totalBytes,
|
|
2953
3219
|
bytesDownloaded: bytesDownloaded - previousFileBytes + (downloadedFiles[fileIndex]?.downloadedBytes ?? 0),
|
|
@@ -2958,6 +3224,7 @@ var LocalModelAssetService = class {
|
|
|
2958
3224
|
await mirrorHubCacheFileForTransformers({
|
|
2959
3225
|
cacheDir: this.cacheDir,
|
|
2960
3226
|
modelId,
|
|
3227
|
+
profileRoot: manifestGroup.rootDir,
|
|
2961
3228
|
filePath: file.path,
|
|
2962
3229
|
cachedPath
|
|
2963
3230
|
});
|
|
@@ -2967,95 +3234,123 @@ var LocalModelAssetService = class {
|
|
|
2967
3234
|
downloadedFiles[fileIndex] = {
|
|
2968
3235
|
path: file.path,
|
|
2969
3236
|
sizeBytes: file.sizeBytes,
|
|
2970
|
-
downloadedBytes: file.sizeBytes
|
|
3237
|
+
downloadedBytes: file.sizeBytes,
|
|
3238
|
+
required: file.required,
|
|
3239
|
+
status: "downloaded"
|
|
2971
3240
|
};
|
|
2972
3241
|
await this.emitDownloadProgress({
|
|
2973
3242
|
modelId,
|
|
3243
|
+
groupId,
|
|
2974
3244
|
sessionId,
|
|
2975
|
-
state,
|
|
2976
3245
|
message: `Downloaded ${file.path}.`,
|
|
2977
3246
|
totalBytes,
|
|
2978
3247
|
bytesDownloaded,
|
|
2979
3248
|
files: downloadedFiles
|
|
2980
3249
|
});
|
|
2981
3250
|
}
|
|
2982
|
-
await this.finishDownload(modelId, sessionId, true, `Local model ${modelId} is ready.`);
|
|
3251
|
+
await this.finishDownload(modelId, groupId, sessionId, true, `Local model ${modelId} is ready.`);
|
|
2983
3252
|
}
|
|
2984
3253
|
async emitDownloadProgress(input) {
|
|
2985
|
-
if (!this.isActiveSession(input.modelId, input.sessionId)) return;
|
|
3254
|
+
if (!this.isActiveSession(input.modelId, input.groupId, input.sessionId)) return;
|
|
2986
3255
|
const progress = input.totalBytes && input.totalBytes > 0 ? Math.max(0, Math.min(1, input.bytesDownloaded / input.totalBytes)) : void 0;
|
|
3256
|
+
const current = await this.readSelectedModelState(input.modelId, input.groupId);
|
|
3257
|
+
const currentGroup = current.groupsState[input.groupId];
|
|
2987
3258
|
const nextState = LocalModelAssetStateSchema.parse({
|
|
2988
|
-
...
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
3259
|
+
...current,
|
|
3260
|
+
groupsState: {
|
|
3261
|
+
...current.groupsState,
|
|
3262
|
+
[input.groupId]: LocalModelLifecycleGroupStateSchema.parse({
|
|
3263
|
+
...currentGroup,
|
|
3264
|
+
groupId: input.groupId,
|
|
3265
|
+
status: "downloading",
|
|
3266
|
+
bytesDownloaded: input.bytesDownloaded,
|
|
3267
|
+
totalBytes: input.totalBytes,
|
|
3268
|
+
progress,
|
|
3269
|
+
resumable: true,
|
|
3270
|
+
files: input.files,
|
|
3271
|
+
updatedAt: this.now()
|
|
3272
|
+
})
|
|
3273
|
+
},
|
|
3274
|
+
updatedAt: this.now()
|
|
2996
3275
|
});
|
|
2997
|
-
await this.
|
|
3276
|
+
const projected = await this.refreshCachedState(nextState, input.groupId, { revalidateDisk: true });
|
|
3277
|
+
await this.store.upsert(projected);
|
|
2998
3278
|
this.emitLog({
|
|
2999
3279
|
engineId: "local",
|
|
3000
3280
|
modelId: input.modelId,
|
|
3001
|
-
selectedGroupId: input.
|
|
3281
|
+
selectedGroupId: input.groupId,
|
|
3282
|
+
groupId: input.groupId,
|
|
3002
3283
|
status: "downloading",
|
|
3003
3284
|
message: input.message,
|
|
3004
3285
|
progress,
|
|
3005
3286
|
bytesDownloaded: input.bytesDownloaded,
|
|
3006
3287
|
totalBytes: input.totalBytes,
|
|
3007
|
-
files: input.files
|
|
3288
|
+
files: input.files.map((file) => ({
|
|
3289
|
+
path: file.path,
|
|
3290
|
+
sizeBytes: file.sizeBytes,
|
|
3291
|
+
downloadedBytes: file.downloadedBytes
|
|
3292
|
+
})),
|
|
3008
3293
|
sessionId: input.sessionId,
|
|
3009
3294
|
resumable: true,
|
|
3010
3295
|
updatedAt: this.now()
|
|
3011
3296
|
});
|
|
3012
3297
|
}
|
|
3013
|
-
async finishDownload(modelId, sessionId, success, message) {
|
|
3014
|
-
if (!this.isActiveSession(modelId, sessionId)) return;
|
|
3015
|
-
const
|
|
3298
|
+
async finishDownload(modelId, groupId, sessionId, success, message) {
|
|
3299
|
+
if (!this.isActiveSession(modelId, groupId, sessionId)) return;
|
|
3300
|
+
const sessionKey = buildSessionKey(modelId, groupId);
|
|
3301
|
+
const current = await this.readSelectedModelState(modelId, groupId);
|
|
3302
|
+
const currentGroup = current.groupsState[groupId];
|
|
3303
|
+
const totalBytes = currentGroup?.totalBytes ?? current.totalBytes;
|
|
3304
|
+
const files = success ? current.files.map((file) => LocalModelLifecycleFileStateSchema.parse({
|
|
3305
|
+
...file,
|
|
3306
|
+
required: true,
|
|
3307
|
+
downloadedBytes: file.sizeBytes,
|
|
3308
|
+
status: "downloaded",
|
|
3309
|
+
updatedAt: this.now()
|
|
3310
|
+
})) : (currentGroup?.files ?? []).map((file) => LocalModelLifecycleFileStateSchema.parse({
|
|
3311
|
+
...file,
|
|
3312
|
+
status: file.status === "downloaded" ? "downloaded" : "paused",
|
|
3313
|
+
updatedAt: this.now()
|
|
3314
|
+
}));
|
|
3016
3315
|
const nextState = LocalModelAssetStateSchema.parse({
|
|
3017
3316
|
...current,
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3317
|
+
groupsState: {
|
|
3318
|
+
...current.groupsState,
|
|
3319
|
+
[groupId]: LocalModelLifecycleGroupStateSchema.parse({
|
|
3320
|
+
...currentGroup,
|
|
3321
|
+
groupId,
|
|
3322
|
+
status: success ? "downloaded" : "error",
|
|
3323
|
+
progress: success ? 1 : current.progress,
|
|
3324
|
+
bytesDownloaded: success ? totalBytes : current.bytesDownloaded,
|
|
3325
|
+
totalBytes,
|
|
3326
|
+
installedAt: success ? this.now() : currentGroup?.installedAt,
|
|
3327
|
+
updatedAt: this.now(),
|
|
3328
|
+
error: success ? void 0 : message,
|
|
3329
|
+
resumable: !success,
|
|
3330
|
+
files
|
|
3331
|
+
})
|
|
3332
|
+
},
|
|
3333
|
+
updatedAt: this.now()
|
|
3026
3334
|
});
|
|
3027
|
-
await this.
|
|
3028
|
-
this.
|
|
3335
|
+
const projected = await this.refreshCachedState(nextState, groupId, { revalidateDisk: true });
|
|
3336
|
+
await this.store.upsert(projected);
|
|
3337
|
+
this.sessions.delete(sessionKey);
|
|
3029
3338
|
this.emitLog({
|
|
3030
3339
|
engineId: "local",
|
|
3031
3340
|
modelId,
|
|
3032
|
-
selectedGroupId:
|
|
3033
|
-
|
|
3341
|
+
selectedGroupId: groupId,
|
|
3342
|
+
groupId,
|
|
3343
|
+
status: projected.status,
|
|
3034
3344
|
message,
|
|
3035
|
-
progress:
|
|
3036
|
-
bytesDownloaded:
|
|
3037
|
-
totalBytes:
|
|
3345
|
+
progress: projected.progress,
|
|
3346
|
+
bytesDownloaded: projected.bytesDownloaded,
|
|
3347
|
+
totalBytes: projected.totalBytes,
|
|
3038
3348
|
sessionId,
|
|
3039
|
-
resumable:
|
|
3040
|
-
files:
|
|
3349
|
+
resumable: projected.resumable,
|
|
3350
|
+
files: projected.files,
|
|
3041
3351
|
updatedAt: this.now()
|
|
3042
3352
|
});
|
|
3043
3353
|
}
|
|
3044
|
-
async readPlan(modelId, transformers, selectedGroupId) {
|
|
3045
|
-
return resolveLocalModelRuntimePlan({
|
|
3046
|
-
modelId,
|
|
3047
|
-
transformers,
|
|
3048
|
-
cacheDir: this.cacheDir,
|
|
3049
|
-
selectedGroupId: selectedGroupId ?? await this.readSelectedGroupId(),
|
|
3050
|
-
hfEndpoint: await this.readHuggingFaceEndpoint(),
|
|
3051
|
-
fetchCacheStore: this.fetchCacheStore
|
|
3052
|
-
}).catch(() => null);
|
|
3053
|
-
}
|
|
3054
|
-
async readPlanForState(modelId, selectedGroupId) {
|
|
3055
|
-
const transformers = await this.getTransformersModule();
|
|
3056
|
-
transformers.env.remoteHost = buildTransformersRemoteHost(await this.readHuggingFaceEndpoint());
|
|
3057
|
-
return this.readPlan(modelId, transformers, selectedGroupId);
|
|
3058
|
-
}
|
|
3059
3354
|
async readSelectedModel() {
|
|
3060
3355
|
return (await this.options.globalSettingsManager.readSettings()).translationEngines.local.model;
|
|
3061
3356
|
}
|
|
@@ -3065,8 +3360,8 @@ var LocalModelAssetService = class {
|
|
|
3065
3360
|
async readHuggingFaceEndpoint() {
|
|
3066
3361
|
return (await this.options.globalSettingsManager.readSettings()).translationEngines.local.hfEndpoint;
|
|
3067
3362
|
}
|
|
3068
|
-
isActiveSession(modelId, sessionId) {
|
|
3069
|
-
return this.sessions.get(modelId)?.sessionId === sessionId;
|
|
3363
|
+
isActiveSession(modelId, groupId, sessionId) {
|
|
3364
|
+
return this.sessions.get(buildSessionKey(modelId, groupId))?.sessionId === sessionId;
|
|
3070
3365
|
}
|
|
3071
3366
|
emitLog(log) {
|
|
3072
3367
|
this.logs.set(log.modelId, log);
|
|
@@ -3080,17 +3375,279 @@ var LocalModelAssetService = class {
|
|
|
3080
3375
|
return import("@huggingface/transformers");
|
|
3081
3376
|
}
|
|
3082
3377
|
};
|
|
3083
|
-
function
|
|
3378
|
+
function buildSessionKey(modelId, groupId) {
|
|
3379
|
+
return `${modelId}::${groupId}`;
|
|
3380
|
+
}
|
|
3381
|
+
function buildVersionedGroupId(baseGroupId, shortCommitHash) {
|
|
3382
|
+
return `${sanitizeId(baseGroupId)}-${sanitizeId(shortCommitHash)}`;
|
|
3383
|
+
}
|
|
3384
|
+
function selectFirstManifestGroupId(manifest) {
|
|
3385
|
+
return manifest?.groupOrder.find((groupId) => manifest.groups[groupId]?.selectable);
|
|
3386
|
+
}
|
|
3387
|
+
function resolveManifestGroupId(manifest, requestedGroupId) {
|
|
3388
|
+
if (!manifest || !requestedGroupId) return requestedGroupId;
|
|
3389
|
+
if (manifest.groups[requestedGroupId]?.selectable) return requestedGroupId;
|
|
3390
|
+
return manifest.groupOrder.find((groupId) => {
|
|
3391
|
+
const group = manifest.groups[groupId];
|
|
3392
|
+
return group?.selectable && group.baseGroupId === requestedGroupId;
|
|
3393
|
+
});
|
|
3394
|
+
}
|
|
3395
|
+
function removeManifestGroup(manifest, groupId) {
|
|
3396
|
+
const groups = { ...manifest.groups };
|
|
3397
|
+
delete groups[groupId];
|
|
3398
|
+
const groupOrder = manifest.groupOrder.filter((id) => id !== groupId);
|
|
3399
|
+
if (groupOrder.length === 0) return void 0;
|
|
3400
|
+
return LocalModelProfileManifestSchema.parse({
|
|
3401
|
+
...manifest,
|
|
3402
|
+
groups,
|
|
3403
|
+
groupOrder
|
|
3404
|
+
});
|
|
3405
|
+
}
|
|
3406
|
+
function removePlanGroup(plan, groupId) {
|
|
3407
|
+
const groups = plan.groups?.filter((group) => group.id !== groupId);
|
|
3408
|
+
if (!groups?.length) return void 0;
|
|
3409
|
+
const selectedGroup = groups.find((group) => group.selected) ?? groups[0];
|
|
3410
|
+
return {
|
|
3411
|
+
...plan,
|
|
3412
|
+
selectedGroupId: selectedGroup?.id,
|
|
3413
|
+
estimatedTotalBytes: selectedGroup?.estimatedTotalBytes,
|
|
3414
|
+
files: selectedGroup?.files ?? [],
|
|
3415
|
+
groups: groups.map((group) => ({
|
|
3416
|
+
...group,
|
|
3417
|
+
selected: group.id === selectedGroup?.id
|
|
3418
|
+
}))
|
|
3419
|
+
};
|
|
3420
|
+
}
|
|
3421
|
+
function migrateLegacyStateToGroups(state, manifest, now) {
|
|
3422
|
+
const selectedGroupId = state.selectedGroupId ?? state.plan?.selectedGroupId;
|
|
3423
|
+
const groupsState = { ...state.groupsState };
|
|
3424
|
+
for (const group of manifest ? [] : state.plan?.groups ?? []) {
|
|
3425
|
+
if (groupsState[group.id] || !group.status || group.status === "not-downloaded") continue;
|
|
3426
|
+
const groupStatus = group.status;
|
|
3427
|
+
const manifestGroup = manifest?.groups[group.id];
|
|
3428
|
+
const groupFiles = group.id === selectedGroupId && state.files.length > 0 ? state.files.map((file) => ({
|
|
3429
|
+
...file,
|
|
3430
|
+
required: true,
|
|
3431
|
+
status: file.sizeBytes !== void 0 && (file.downloadedBytes ?? 0) >= file.sizeBytes ? "downloaded" : normalizeLiveStatusForStoredState(groupStatus)
|
|
3432
|
+
})) : group.files.map((file) => ({
|
|
3433
|
+
...file,
|
|
3434
|
+
downloadedBytes: groupStatus === "downloaded" ? file.sizeBytes : 0,
|
|
3435
|
+
status: normalizeLiveStatusForStoredState(groupStatus)
|
|
3436
|
+
}));
|
|
3437
|
+
const bytesDownloaded = sumDownloadedBytes(groupFiles);
|
|
3438
|
+
const totalBytes = group.estimatedTotalBytes;
|
|
3439
|
+
groupsState[group.id] = LocalModelLifecycleGroupStateSchema.parse({
|
|
3440
|
+
groupId: group.id,
|
|
3441
|
+
baseGroupId: group.baseGroupId ?? manifestGroup?.baseGroupId ?? group.id,
|
|
3442
|
+
status: normalizeLiveStatusForStoredState(groupStatus),
|
|
3443
|
+
rootDir: group.rootDir ?? manifestGroup?.rootDir,
|
|
3444
|
+
bytesDownloaded,
|
|
3445
|
+
totalBytes,
|
|
3446
|
+
progress: totalBytes && totalBytes > 0 ? Math.max(0, Math.min(1, bytesDownloaded / totalBytes)) : group.progress,
|
|
3447
|
+
resumable: group.resumable ?? (groupStatus === "paused" || groupStatus === "downloading" || groupStatus === "queued" || groupStatus === "error"),
|
|
3448
|
+
error: group.error,
|
|
3449
|
+
installedAt: groupStatus === "downloaded" ? state.installedAt ?? now : void 0,
|
|
3450
|
+
updatedAt: state.updatedAt ?? now,
|
|
3451
|
+
files: groupFiles
|
|
3452
|
+
});
|
|
3453
|
+
}
|
|
3454
|
+
if (selectedGroupId && !groupsState[selectedGroupId] && state.files.length > 0 && state.status !== "not-downloaded") {
|
|
3455
|
+
const manifestGroup = manifest?.groups[selectedGroupId];
|
|
3456
|
+
groupsState[selectedGroupId] = LocalModelLifecycleGroupStateSchema.parse({
|
|
3457
|
+
groupId: selectedGroupId,
|
|
3458
|
+
baseGroupId: manifestGroup?.baseGroupId ?? selectedGroupId,
|
|
3459
|
+
status: normalizeLiveStatusForStoredState(state.status),
|
|
3460
|
+
rootDir: manifestGroup?.rootDir,
|
|
3461
|
+
bytesDownloaded: state.bytesDownloaded,
|
|
3462
|
+
totalBytes: state.totalBytes,
|
|
3463
|
+
progress: state.progress,
|
|
3464
|
+
resumable: state.resumable,
|
|
3465
|
+
error: state.error,
|
|
3466
|
+
installedAt: state.installedAt,
|
|
3467
|
+
updatedAt: state.updatedAt ?? now,
|
|
3468
|
+
files: state.files.map((file) => LocalModelLifecycleFileStateSchema.parse({
|
|
3469
|
+
...file,
|
|
3470
|
+
required: true,
|
|
3471
|
+
status: file.sizeBytes !== void 0 && (file.downloadedBytes ?? 0) >= file.sizeBytes ? "downloaded" : state.status === "downloaded" ? "downloaded" : normalizeLiveStatusForStoredState(state.status)
|
|
3472
|
+
}))
|
|
3473
|
+
});
|
|
3474
|
+
}
|
|
3475
|
+
return LocalModelAssetStateSchema.parse({
|
|
3476
|
+
...state,
|
|
3477
|
+
selectedGroupId,
|
|
3478
|
+
groupsState
|
|
3479
|
+
});
|
|
3480
|
+
}
|
|
3481
|
+
function mergeHistoricalGroupsIntoManifest(input) {
|
|
3482
|
+
const existing = input.manifest;
|
|
3483
|
+
const groups = existing ? { ...existing.groups } : {};
|
|
3484
|
+
const groupOrder = existing ? [...existing.groupOrder] : [];
|
|
3485
|
+
const fallbackGroups = input.fallbackPlan?.groups ?? [];
|
|
3486
|
+
for (const fallbackGroup of fallbackGroups) {
|
|
3487
|
+
if (groups[fallbackGroup.id]) continue;
|
|
3488
|
+
if (!isConcreteCommitHash(fallbackGroup.commitHash) || !isConcreteCommitHash(fallbackGroup.shortCommitHash)) continue;
|
|
3489
|
+
const state = input.groupsState[fallbackGroup.id];
|
|
3490
|
+
const commitHash = fallbackGroup.commitHash;
|
|
3491
|
+
const shortCommitHash = fallbackGroup.shortCommitHash;
|
|
3492
|
+
groups[fallbackGroup.id] = {
|
|
3493
|
+
id: fallbackGroup.id,
|
|
3494
|
+
baseGroupId: fallbackGroup.baseGroupId ?? fallbackGroup.id,
|
|
3495
|
+
label: fallbackGroup.label,
|
|
3496
|
+
displayLabel: `${fallbackGroup.label} · ${shortCommitHash}`,
|
|
3497
|
+
description: fallbackGroup.description,
|
|
3498
|
+
profile: fallbackGroup.profile,
|
|
3499
|
+
dtype: fallbackGroup.dtype,
|
|
3500
|
+
commitHash,
|
|
3501
|
+
shortCommitHash,
|
|
3502
|
+
rootDir: fallbackGroup.rootDir ?? state?.rootDir ?? getLocalModelProfileGroupRoot(input.cacheDir, input.modelId, fallbackGroup.id),
|
|
3503
|
+
estimatedTotalBytes: fallbackGroup.estimatedTotalBytes,
|
|
3504
|
+
selectable: fallbackGroup.selectable,
|
|
3505
|
+
files: fallbackGroup.files.map((file) => ({
|
|
3506
|
+
...file,
|
|
3507
|
+
revision: file.revision ?? commitHash
|
|
3508
|
+
}))
|
|
3509
|
+
};
|
|
3510
|
+
groupOrder.push(fallbackGroup.id);
|
|
3511
|
+
}
|
|
3512
|
+
if (!existing && groupOrder.length === 0) return void 0;
|
|
3513
|
+
return LocalModelProfileManifestSchema.parse({
|
|
3514
|
+
modelId: input.modelId,
|
|
3515
|
+
source: "huggingface",
|
|
3516
|
+
endpoint: existing?.endpoint ?? "",
|
|
3517
|
+
revision: existing?.revision ?? "legacy",
|
|
3518
|
+
commitHash: existing?.commitHash ?? "legacy",
|
|
3519
|
+
shortCommitHash: existing?.shortCommitHash ?? "legacy",
|
|
3520
|
+
fetchedAt: existing?.fetchedAt ?? 0,
|
|
3521
|
+
updatedAt: existing?.updatedAt ?? 0,
|
|
3522
|
+
raw: existing?.raw,
|
|
3523
|
+
groups,
|
|
3524
|
+
groupOrder
|
|
3525
|
+
});
|
|
3526
|
+
}
|
|
3527
|
+
function filterConcreteProfileManifest(manifest) {
|
|
3528
|
+
if (!manifest || !isConcreteCommitHash(manifest.commitHash)) return void 0;
|
|
3529
|
+
const groups = Object.fromEntries(manifest.groupOrder.flatMap((groupId) => {
|
|
3530
|
+
const group = manifest.groups[groupId];
|
|
3531
|
+
if (!group || !isConcreteCommitHash(group.commitHash)) return [];
|
|
3532
|
+
return [[groupId, group]];
|
|
3533
|
+
}));
|
|
3534
|
+
const groupOrder = manifest.groupOrder.filter((groupId) => groups[groupId]);
|
|
3535
|
+
if (groupOrder.length === 0) return void 0;
|
|
3536
|
+
return LocalModelProfileManifestSchema.parse({
|
|
3537
|
+
...manifest,
|
|
3538
|
+
groups,
|
|
3539
|
+
groupOrder
|
|
3540
|
+
});
|
|
3541
|
+
}
|
|
3542
|
+
function isConcreteCommitHash(value) {
|
|
3543
|
+
return Boolean(value && value !== "legacy");
|
|
3544
|
+
}
|
|
3545
|
+
function formatManifestGroupChipLabel(manifest, group) {
|
|
3546
|
+
if (group.commitHash === manifest.commitHash) return group.label;
|
|
3547
|
+
return `${group.label} · ${group.shortCommitHash}`;
|
|
3548
|
+
}
|
|
3549
|
+
function buildPlanFromManifest(input) {
|
|
3550
|
+
const manifest = input.manifest;
|
|
3551
|
+
if (!manifest) return null;
|
|
3552
|
+
const selectedGroupId = input.selectedGroupId && manifest.groups[input.selectedGroupId]?.selectable ? input.selectedGroupId : selectFirstManifestGroupId(manifest);
|
|
3553
|
+
const groups = manifest.groupOrder.flatMap((groupId) => {
|
|
3554
|
+
const manifestGroup = manifest.groups[groupId];
|
|
3555
|
+
if (!manifestGroup) return [];
|
|
3556
|
+
const groupState = input.groupsState[groupId];
|
|
3557
|
+
return [{
|
|
3558
|
+
id: manifestGroup.id,
|
|
3559
|
+
label: formatManifestGroupChipLabel(manifest, manifestGroup),
|
|
3560
|
+
description: manifestGroup.description,
|
|
3561
|
+
profile: manifestGroup.profile,
|
|
3562
|
+
dtype: manifestGroup.dtype,
|
|
3563
|
+
estimatedTotalBytes: manifestGroup.estimatedTotalBytes,
|
|
3564
|
+
baseGroupId: manifestGroup.baseGroupId,
|
|
3565
|
+
commitHash: manifestGroup.commitHash,
|
|
3566
|
+
shortCommitHash: manifestGroup.shortCommitHash,
|
|
3567
|
+
rootDir: manifestGroup.rootDir,
|
|
3568
|
+
status: groupState?.status ?? "not-downloaded",
|
|
3569
|
+
progress: groupState?.progress,
|
|
3570
|
+
bytesDownloaded: groupState?.bytesDownloaded,
|
|
3571
|
+
totalBytes: groupState?.totalBytes ?? manifestGroup.estimatedTotalBytes,
|
|
3572
|
+
resumable: groupState?.resumable,
|
|
3573
|
+
error: groupState?.error,
|
|
3574
|
+
selectable: manifestGroup.selectable,
|
|
3575
|
+
selected: manifestGroup.id === selectedGroupId,
|
|
3576
|
+
files: manifestGroup.files.map((file) => ({
|
|
3577
|
+
...file,
|
|
3578
|
+
required: file.required
|
|
3579
|
+
}))
|
|
3580
|
+
}];
|
|
3581
|
+
});
|
|
3582
|
+
const selectedGroup = groups.find((group) => group.id === selectedGroupId) ?? groups[0];
|
|
3583
|
+
if (!selectedGroup) return null;
|
|
3584
|
+
return {
|
|
3585
|
+
modelId: input.modelId,
|
|
3586
|
+
estimatedTotalBytes: selectedGroup.estimatedTotalBytes,
|
|
3587
|
+
files: selectedGroup.files,
|
|
3588
|
+
selectedGroupId: selectedGroup.id,
|
|
3589
|
+
groups
|
|
3590
|
+
};
|
|
3591
|
+
}
|
|
3592
|
+
function reconcileGroupFiles(input) {
|
|
3084
3593
|
const currentFileByPath = new Map(input.currentFiles.map((file) => [file.path, file]));
|
|
3085
|
-
return input.
|
|
3086
|
-
const
|
|
3087
|
-
|
|
3594
|
+
return input.manifestGroup.files.map((file) => {
|
|
3595
|
+
const current = currentFileByPath.get(file.path);
|
|
3596
|
+
const downloadedBytes = current?.downloadedBytes === void 0 ? 0 : file.sizeBytes === void 0 ? current.downloadedBytes : Math.min(current.downloadedBytes, file.sizeBytes);
|
|
3597
|
+
const status = file.sizeBytes !== void 0 && downloadedBytes >= file.sizeBytes ? "downloaded" : current?.status ?? "not-downloaded";
|
|
3598
|
+
return LocalModelLifecycleFileStateSchema.parse({
|
|
3088
3599
|
path: file.path,
|
|
3089
3600
|
sizeBytes: file.sizeBytes,
|
|
3090
|
-
downloadedBytes
|
|
3091
|
-
|
|
3601
|
+
downloadedBytes,
|
|
3602
|
+
required: file.required,
|
|
3603
|
+
status,
|
|
3604
|
+
updatedAt: current?.updatedAt,
|
|
3605
|
+
error: current?.error
|
|
3606
|
+
});
|
|
3092
3607
|
});
|
|
3093
3608
|
}
|
|
3609
|
+
function reconcileGroupFilesFromSnapshot(input) {
|
|
3610
|
+
const currentFileByPath = new Map(input.currentFiles.map((file) => [file.path, file]));
|
|
3611
|
+
return input.manifestGroup.files.map((file) => {
|
|
3612
|
+
const current = currentFileByPath.get(file.path);
|
|
3613
|
+
const downloadedBytes = current?.downloadedBytes === void 0 ? input.currentStatus === "downloaded" ? file.sizeBytes : 0 : file.sizeBytes === void 0 ? current.downloadedBytes : Math.min(current.downloadedBytes, file.sizeBytes);
|
|
3614
|
+
const status = current?.status ?? (file.sizeBytes !== void 0 && downloadedBytes !== void 0 && downloadedBytes >= file.sizeBytes ? "downloaded" : input.currentStatus === "downloaded" ? "downloaded" : input.currentStatus === "paused" || input.currentStatus === "downloading" || input.currentStatus === "error" ? input.currentStatus : "not-downloaded");
|
|
3615
|
+
return LocalModelLifecycleFileStateSchema.parse({
|
|
3616
|
+
path: file.path,
|
|
3617
|
+
sizeBytes: file.sizeBytes,
|
|
3618
|
+
downloadedBytes,
|
|
3619
|
+
required: file.required,
|
|
3620
|
+
status,
|
|
3621
|
+
updatedAt: current?.updatedAt,
|
|
3622
|
+
error: current?.error
|
|
3623
|
+
});
|
|
3624
|
+
});
|
|
3625
|
+
}
|
|
3626
|
+
async function reconcileGroupFilesFromDisk(input) {
|
|
3627
|
+
const currentFileByPath = new Map(input.currentFiles.map((file) => [file.path, file]));
|
|
3628
|
+
return Promise.all(input.manifestGroup.files.map(async (file) => {
|
|
3629
|
+
const current = currentFileByPath.get(file.path);
|
|
3630
|
+
const diskBytes = await readPathSize(join(input.rootDir, file.path));
|
|
3631
|
+
const downloadedBytes = diskBytes === null ? current?.downloadedBytes ?? 0 : file.sizeBytes === void 0 ? diskBytes : Math.min(diskBytes, file.sizeBytes);
|
|
3632
|
+
const status = file.sizeBytes !== void 0 && downloadedBytes >= file.sizeBytes ? "downloaded" : downloadedBytes > 0 ? "paused" : "not-downloaded";
|
|
3633
|
+
return LocalModelLifecycleFileStateSchema.parse({
|
|
3634
|
+
path: file.path,
|
|
3635
|
+
sizeBytes: file.sizeBytes,
|
|
3636
|
+
downloadedBytes,
|
|
3637
|
+
required: file.required,
|
|
3638
|
+
status,
|
|
3639
|
+
updatedAt: current?.updatedAt,
|
|
3640
|
+
error: current?.error
|
|
3641
|
+
});
|
|
3642
|
+
}));
|
|
3643
|
+
}
|
|
3644
|
+
function isActiveDownloadStatus(status) {
|
|
3645
|
+
return status === "queued" || status === "downloading" || status === "deleting";
|
|
3646
|
+
}
|
|
3647
|
+
function normalizeLiveStatusForStoredState(status) {
|
|
3648
|
+
if (status === "queued" || status === "downloading") return "paused";
|
|
3649
|
+
return status;
|
|
3650
|
+
}
|
|
3094
3651
|
function sumDownloadedBytes(files) {
|
|
3095
3652
|
return files.reduce((total, file) => {
|
|
3096
3653
|
const downloadedBytes = file.downloadedBytes ?? 0;
|
|
@@ -3118,12 +3675,11 @@ function throwIfAborted(signal) {
|
|
|
3118
3675
|
if (signal.aborted) throw new Error("Local model download aborted.");
|
|
3119
3676
|
}
|
|
3120
3677
|
async function downloadHuggingFaceFileToCacheDirWithProgress(input) {
|
|
3121
|
-
const revision = "main";
|
|
3122
3678
|
let lastError;
|
|
3123
3679
|
const info = await readHuggingFaceFileDownloadInfoWithRetry({
|
|
3124
3680
|
repo: input.repo,
|
|
3125
3681
|
path: input.path,
|
|
3126
|
-
revision,
|
|
3682
|
+
revision: input.revision,
|
|
3127
3683
|
hubUrl: input.hubUrl,
|
|
3128
3684
|
retryPolicy: input.retryPolicy,
|
|
3129
3685
|
fetch: input.fetch,
|
|
@@ -3137,25 +3693,25 @@ async function downloadHuggingFaceFileToCacheDirWithProgress(input) {
|
|
|
3137
3693
|
cacheDir: input.cacheDir,
|
|
3138
3694
|
modelId: input.repo.name,
|
|
3139
3695
|
filePath: input.path,
|
|
3140
|
-
revision,
|
|
3141
|
-
etag: info.etag
|
|
3696
|
+
revision: input.revision,
|
|
3697
|
+
etag: input.etag ?? info.etag
|
|
3142
3698
|
});
|
|
3143
|
-
const
|
|
3144
|
-
if (
|
|
3699
|
+
const existingTargetSize = await readPathSize(input.targetPath);
|
|
3700
|
+
if (existingTargetSize !== null && existingTargetSize >= totalBytes) {
|
|
3145
3701
|
await input.onProgress(totalBytes);
|
|
3146
|
-
return
|
|
3702
|
+
return input.targetPath;
|
|
3147
3703
|
}
|
|
3148
3704
|
for (let attempt = 0; attempt <= input.retryPolicy.limit; attempt += 1) try {
|
|
3149
3705
|
throwIfAborted(input.signal);
|
|
3150
|
-
let resumeBytes = await readPathSize(
|
|
3706
|
+
let resumeBytes = await readPathSize(`${input.targetPath}.incomplete`);
|
|
3151
3707
|
if (resumeBytes !== null && resumeBytes > totalBytes) {
|
|
3152
|
-
await rm(
|
|
3708
|
+
await rm(`${input.targetPath}.incomplete`, { force: true });
|
|
3153
3709
|
resumeBytes = 0;
|
|
3154
3710
|
}
|
|
3155
3711
|
if (resumeBytes !== null && resumeBytes > 0) await input.onProgress(Math.min(resumeBytes, totalBytes));
|
|
3156
3712
|
if (!await streamDownloadToIncompleteFile({
|
|
3157
3713
|
url: info.url,
|
|
3158
|
-
incompletePath:
|
|
3714
|
+
incompletePath: `${input.targetPath}.incomplete`,
|
|
3159
3715
|
startBytes: resumeBytes ?? 0,
|
|
3160
3716
|
totalBytes,
|
|
3161
3717
|
accessToken: void 0,
|
|
@@ -3166,7 +3722,7 @@ async function downloadHuggingFaceFileToCacheDirWithProgress(input) {
|
|
|
3166
3722
|
const blob = await downloadFile({
|
|
3167
3723
|
repo: input.repo,
|
|
3168
3724
|
path: input.path,
|
|
3169
|
-
revision,
|
|
3725
|
+
revision: input.revision,
|
|
3170
3726
|
hubUrl: input.hubUrl,
|
|
3171
3727
|
fetch: input.fetch,
|
|
3172
3728
|
downloadInfo: info,
|
|
@@ -3175,15 +3731,24 @@ async function downloadHuggingFaceFileToCacheDirWithProgress(input) {
|
|
|
3175
3731
|
if (!blob) throw new Error(`Invalid response for file ${input.path}.`);
|
|
3176
3732
|
await appendBlobToIncompleteFile({
|
|
3177
3733
|
blob: resumeBytes && resumeBytes > 0 ? blob.slice(resumeBytes, totalBytes) : blob,
|
|
3178
|
-
incompletePath:
|
|
3734
|
+
incompletePath: `${input.targetPath}.incomplete`,
|
|
3179
3735
|
startBytes: resumeBytes ?? 0,
|
|
3180
3736
|
totalBytes,
|
|
3181
3737
|
onProgress: input.onProgress
|
|
3182
3738
|
});
|
|
3183
3739
|
}
|
|
3184
|
-
await
|
|
3740
|
+
const incompleteSize = await readPathSize(`${input.targetPath}.incomplete`);
|
|
3741
|
+
if (incompleteSize === null || incompleteSize < totalBytes) throw new Error(`Incomplete response for file ${input.path}: downloaded ${incompleteSize ?? 0} of ${totalBytes} bytes.`);
|
|
3742
|
+
await finalizeDownloadedFile({
|
|
3743
|
+
incompletePath: `${input.targetPath}.incomplete`,
|
|
3744
|
+
targetPath: input.targetPath
|
|
3745
|
+
});
|
|
3746
|
+
await mirrorDownloadedFileToHubCache({
|
|
3747
|
+
targetPath: input.targetPath,
|
|
3748
|
+
cachePaths
|
|
3749
|
+
});
|
|
3185
3750
|
await input.onProgress(totalBytes);
|
|
3186
|
-
return
|
|
3751
|
+
return input.targetPath;
|
|
3187
3752
|
} catch (error) {
|
|
3188
3753
|
lastError = error;
|
|
3189
3754
|
if (!isRetryableDownloadError(error) || attempt === input.retryPolicy.limit) throw error;
|
|
@@ -3266,13 +3831,18 @@ async function streamDownloadToIncompleteFile(input) {
|
|
|
3266
3831
|
}
|
|
3267
3832
|
return true;
|
|
3268
3833
|
}
|
|
3269
|
-
async function
|
|
3270
|
-
await mkdir(dirname(input.
|
|
3271
|
-
await
|
|
3272
|
-
await
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
await
|
|
3834
|
+
async function finalizeDownloadedFile(input) {
|
|
3835
|
+
await mkdir(dirname(input.targetPath), { recursive: true });
|
|
3836
|
+
await rm(input.targetPath, { force: true });
|
|
3837
|
+
await rename(input.incompletePath, input.targetPath);
|
|
3838
|
+
}
|
|
3839
|
+
async function mirrorDownloadedFileToHubCache(input) {
|
|
3840
|
+
await mkdir(dirname(input.cachePaths.blobPath), { recursive: true });
|
|
3841
|
+
await mkdir(dirname(input.cachePaths.pointerPath), { recursive: true });
|
|
3842
|
+
await rm(input.cachePaths.blobPath, { force: true });
|
|
3843
|
+
await copyFile(input.targetPath, input.cachePaths.blobPath);
|
|
3844
|
+
await unlink(input.cachePaths.pointerPath).catch(() => void 0);
|
|
3845
|
+
await symlink(input.cachePaths.blobPath, input.cachePaths.pointerPath);
|
|
3276
3846
|
}
|
|
3277
3847
|
function getHubCacheFilePaths(input) {
|
|
3278
3848
|
const repoPath = getHubCacheRepoPath(input.cacheDir, input.modelId);
|
|
@@ -3322,6 +3892,7 @@ function formatDuration(ms) {
|
|
|
3322
3892
|
}
|
|
3323
3893
|
async function mirrorHubCacheFileForTransformers(input) {
|
|
3324
3894
|
const sourcePath = await resolveRealCacheFile(input.cachedPath);
|
|
3895
|
+
await copyFileIfMissing(sourcePath, join(input.profileRoot, input.filePath));
|
|
3325
3896
|
await copyFileIfMissing(sourcePath, join(getTransformersLocalModelPath(input.cacheDir, input.modelId), input.filePath));
|
|
3326
3897
|
await copyFileIfMissing(sourcePath, join(getTransformersFileCacheModelPath(input.cacheDir, input.modelId), input.filePath));
|
|
3327
3898
|
}
|
|
@@ -3341,9 +3912,11 @@ function getHubCacheRepoPath(cacheDir, modelId) {
|
|
|
3341
3912
|
return join(cacheDir, `models--${modelId.split("/").join("--")}`);
|
|
3342
3913
|
}
|
|
3343
3914
|
function toCatalogItem(candidate, asset) {
|
|
3344
|
-
const
|
|
3915
|
+
const downloadGroups = asset.plan?.groups ?? candidate.downloadGroups;
|
|
3916
|
+
const hasSelectableGroup = downloadGroups?.some((group) => group.selectable) ?? false;
|
|
3345
3917
|
return {
|
|
3346
3918
|
...candidate,
|
|
3919
|
+
downloadGroups,
|
|
3347
3920
|
asset,
|
|
3348
3921
|
selectable: hasSelectableGroup || (candidate.size.estimatedTotalBytes ?? 0) > 0,
|
|
3349
3922
|
local: asset.status === "downloaded" || asset.status === "paused" || asset.status === "downloading" || (asset.progress ?? 0) > 0
|
|
@@ -5022,7 +5595,7 @@ const globalSettingsRouter = router({
|
|
|
5022
5595
|
translationCache: TranslationCacheSettingsSchema.partial().optional(),
|
|
5023
5596
|
translationEngines: z.object({
|
|
5024
5597
|
openai: TranslationOpenAISettingsSchema.partial().optional(),
|
|
5025
|
-
local: TranslationLocalSettingsSchema.partial().optional()
|
|
5598
|
+
local: TranslationLocalSettingsSchema.partial().extend({ selectedGroupId: z.string().min(1).nullable().optional() }).optional()
|
|
5026
5599
|
}).optional()
|
|
5027
5600
|
})).mutation(async ({ ctx, input }) => {
|
|
5028
5601
|
await ctx.globalSettingsManager.writeSettings(input);
|
|
@@ -5113,6 +5686,18 @@ const localModelsRouter = router({
|
|
|
5113
5686
|
})).query(({ ctx, input }) => {
|
|
5114
5687
|
return ctx.localModelAssetService.readSelectedModelState(input.modelId, input.selectedGroupId);
|
|
5115
5688
|
}),
|
|
5689
|
+
panelState: publicProcedure.input(z.object({
|
|
5690
|
+
modelId: z.string().min(1),
|
|
5691
|
+
selectedGroupId: z.string().min(1).optional()
|
|
5692
|
+
})).query(async ({ ctx, input }) => {
|
|
5693
|
+
const asset = await ctx.localModelAssetService.readSelectedModelState(input.modelId, input.selectedGroupId);
|
|
5694
|
+
return {
|
|
5695
|
+
modelId: input.modelId,
|
|
5696
|
+
selectedGroupId: input.selectedGroupId ?? asset.plan?.selectedGroupId,
|
|
5697
|
+
asset,
|
|
5698
|
+
downloadPlan: asset.plan ?? null
|
|
5699
|
+
};
|
|
5700
|
+
}),
|
|
5116
5701
|
subscribeLogs: publicProcedure.subscription(({ ctx }) => {
|
|
5117
5702
|
return ctx.localModelAssetService.subscribeLogs();
|
|
5118
5703
|
}),
|
|
@@ -5122,21 +5707,40 @@ const localModelsRouter = router({
|
|
|
5122
5707
|
}),
|
|
5123
5708
|
download: publicProcedure.input(z.object({
|
|
5124
5709
|
modelId: z.string().min(1),
|
|
5710
|
+
groupId: z.string().min(1).optional(),
|
|
5125
5711
|
selectedGroupId: z.string().min(1).optional()
|
|
5126
5712
|
})).mutation(async ({ ctx, input }) => {
|
|
5127
|
-
return ctx.localModelAssetService.startDownload(input.modelId, input.selectedGroupId);
|
|
5713
|
+
return ctx.localModelAssetService.startDownload(input.modelId, input.groupId ?? input.selectedGroupId);
|
|
5128
5714
|
}),
|
|
5129
|
-
pause: publicProcedure.input(z.object({
|
|
5130
|
-
|
|
5715
|
+
pause: publicProcedure.input(z.object({
|
|
5716
|
+
modelId: z.string().min(1),
|
|
5717
|
+
groupId: z.string().min(1).optional(),
|
|
5718
|
+
selectedGroupId: z.string().min(1).optional()
|
|
5719
|
+
})).mutation(({ ctx, input }) => {
|
|
5720
|
+
return ctx.localModelAssetService.pauseDownload(input.modelId, input.groupId ?? input.selectedGroupId);
|
|
5131
5721
|
}),
|
|
5132
5722
|
resume: publicProcedure.input(z.object({
|
|
5133
5723
|
modelId: z.string().min(1),
|
|
5724
|
+
groupId: z.string().min(1).optional(),
|
|
5134
5725
|
selectedGroupId: z.string().min(1).optional()
|
|
5135
5726
|
})).mutation(async ({ ctx, input }) => {
|
|
5136
|
-
return ctx.localModelAssetService.resumeDownload(input.modelId, input.selectedGroupId);
|
|
5727
|
+
return ctx.localModelAssetService.resumeDownload(input.modelId, input.groupId ?? input.selectedGroupId);
|
|
5137
5728
|
}),
|
|
5138
|
-
delete: publicProcedure.input(z.object({
|
|
5139
|
-
|
|
5729
|
+
delete: publicProcedure.input(z.object({
|
|
5730
|
+
modelId: z.string().min(1),
|
|
5731
|
+
groupId: z.string().min(1).optional(),
|
|
5732
|
+
selectedGroupId: z.string().min(1).optional()
|
|
5733
|
+
})).mutation(({ ctx, input }) => {
|
|
5734
|
+
return ctx.localModelAssetService.deleteModel(input.modelId, input.groupId ?? input.selectedGroupId);
|
|
5735
|
+
}),
|
|
5736
|
+
refreshProfiles: publicProcedure.input(z.object({ modelId: z.string().min(1).optional() })).mutation(async ({ ctx, input }) => {
|
|
5737
|
+
const asset = await ctx.localModelAssetService.refreshProfiles(input.modelId);
|
|
5738
|
+
return {
|
|
5739
|
+
modelId: asset.modelId,
|
|
5740
|
+
selectedGroupId: asset.selectedGroupId ?? asset.plan?.selectedGroupId,
|
|
5741
|
+
asset,
|
|
5742
|
+
downloadPlan: asset.plan ?? null
|
|
5743
|
+
};
|
|
5140
5744
|
})
|
|
5141
5745
|
});
|
|
5142
5746
|
const OPSX_CORE_PROFILE_WORKFLOWS = [
|
|
@@ -5632,7 +6236,7 @@ const configRouter = router({
|
|
|
5632
6236
|
translation: DocumentTranslationConfigSchema.partial().extend({ engines: z.object({
|
|
5633
6237
|
local: z.object({
|
|
5634
6238
|
model: z.string().min(1).optional(),
|
|
5635
|
-
selectedGroupId: z.string().min(1).optional()
|
|
6239
|
+
selectedGroupId: z.string().min(1).nullable().optional()
|
|
5636
6240
|
}).optional(),
|
|
5637
6241
|
openai: z.object({ model: z.string().min(1).optional() }).optional()
|
|
5638
6242
|
}).optional() }).optional()
|
|
@@ -6667,22 +7271,19 @@ var TranslationCacheService = class {
|
|
|
6667
7271
|
//#endregion
|
|
6668
7272
|
//#region src/translation-engine-service.ts
|
|
6669
7273
|
var TranslationEngineService = class {
|
|
6670
|
-
projectDir;
|
|
6671
7274
|
configManager;
|
|
6672
7275
|
globalSettingsManager;
|
|
6673
7276
|
now;
|
|
6674
7277
|
localCacheDir;
|
|
6675
7278
|
localAssetStore;
|
|
6676
|
-
localFetchCacheStore;
|
|
6677
7279
|
constructor(options) {
|
|
6678
7280
|
ensureProxyAwareFetchDispatcher();
|
|
6679
|
-
this.projectDir = options.projectDir;
|
|
6680
7281
|
this.configManager = options.configManager;
|
|
6681
7282
|
this.globalSettingsManager = options.globalSettingsManager;
|
|
6682
7283
|
this.now = options.now ?? Date.now;
|
|
6683
7284
|
this.localCacheDir = options.localCacheDir ?? getDefaultLocalModelCacheDir();
|
|
6684
7285
|
this.localAssetStore = new LocalModelAssetStore({ indexPath: options.localAssetIndexPath ?? getDefaultLocalModelIndexPath() });
|
|
6685
|
-
|
|
7286
|
+
new LocalModelFetchCacheStore({
|
|
6686
7287
|
cachePath: options.localFetchCachePath ?? getDefaultLocalModelFetchCachePath(),
|
|
6687
7288
|
now: this.now
|
|
6688
7289
|
});
|
|
@@ -6702,20 +7303,7 @@ var TranslationEngineService = class {
|
|
|
6702
7303
|
}
|
|
6703
7304
|
async getModelDownloadPlan(input) {
|
|
6704
7305
|
if (input.engineId !== "local") return null;
|
|
6705
|
-
|
|
6706
|
-
const plan = await resolveLocalModelRuntimePlanFromProject({
|
|
6707
|
-
projectDir: this.projectDir,
|
|
6708
|
-
globalSettingsManager: this.globalSettingsManager,
|
|
6709
|
-
modelId: input.model,
|
|
6710
|
-
selectedGroupId: input.selectedGroupId,
|
|
6711
|
-
cacheDir: this.localCacheDir,
|
|
6712
|
-
fetchCacheStore: this.localFetchCacheStore,
|
|
6713
|
-
loadTransformersModule: this.loadLocalTransformersModuleForPlan.bind(this)
|
|
6714
|
-
}).catch(() => null);
|
|
6715
|
-
const fallbackPlan = selectPersistedLocalPlan(state, input.selectedGroupId);
|
|
6716
|
-
const effectivePlan = plan ?? fallbackPlan;
|
|
6717
|
-
if (!effectivePlan) return null;
|
|
6718
|
-
return enrichDownloadPlanWithAssetSnapshot(effectivePlan, state, input.selectedGroupId);
|
|
7306
|
+
return selectPersistedLocalPlan((await this.localAssetStore.readMap()).get(input.model), input.selectedGroupId);
|
|
6719
7307
|
}
|
|
6720
7308
|
async selectEngine(engineId) {
|
|
6721
7309
|
await this.configManager.writeConfig({ translation: { engineId } });
|
|
@@ -6731,14 +7319,26 @@ var TranslationEngineService = class {
|
|
|
6731
7319
|
(async () => {
|
|
6732
7320
|
try {
|
|
6733
7321
|
if (input.engineId === "browser") throw new Error("Browser translator runs in the browser runtime.");
|
|
6734
|
-
const
|
|
6735
|
-
|
|
6736
|
-
|
|
7322
|
+
const settingsSnapshot = await this.globalSettingsManager.readSettings();
|
|
7323
|
+
const effectiveModel = resolveBatchTranslateModel(input, settingsSnapshot);
|
|
7324
|
+
if (input.engineId === "local") {
|
|
7325
|
+
const directionCheck = checkLocalDirectionalModelLanguagePair({
|
|
7326
|
+
model: effectiveModel,
|
|
7327
|
+
sourceLanguage: input.sourceLanguage,
|
|
7328
|
+
targetLanguage: input.targetLanguage
|
|
7329
|
+
});
|
|
7330
|
+
if (!directionCheck.supported) throw new Error(directionCheck.message ?? "Selected local model does not support the requested translation direction.");
|
|
7331
|
+
}
|
|
7332
|
+
const effectiveSelectedGroupId = input.engineId === "local" ? input.selectedGroupId ?? settingsSnapshot.translationEngines.local.selectedGroupId : void 0;
|
|
7333
|
+
const dtype = await this.readLocalDtype(input.engineId, effectiveModel, effectiveSelectedGroupId);
|
|
7334
|
+
if (input.engineId === "local" && effectiveModel) await this.assertLocalModelReady(effectiveModel, effectiveSelectedGroupId);
|
|
7335
|
+
const runtimeConfig = input.engineId === "local" && effectiveModel ? await this.readLocalRuntimeConfig(effectiveModel, effectiveSelectedGroupId) : void 0;
|
|
7336
|
+
const translator = await (await this.loadFactory(input.engineId, effectiveModel, settingsSnapshot)).create({
|
|
6737
7337
|
sourceLanguage: input.sourceLanguage,
|
|
6738
7338
|
targetLanguage: input.targetLanguage,
|
|
6739
|
-
model:
|
|
7339
|
+
model: effectiveModel,
|
|
6740
7340
|
dtype,
|
|
6741
|
-
runtimeConfig
|
|
7341
|
+
runtimeConfig,
|
|
6742
7342
|
signal: controller.signal
|
|
6743
7343
|
});
|
|
6744
7344
|
try {
|
|
@@ -6764,11 +7364,11 @@ var TranslationEngineService = class {
|
|
|
6764
7364
|
if (engineId !== "local" || !model) return void 0;
|
|
6765
7365
|
const effectiveSelectedGroupId = selectedGroupId ?? (await this.globalSettingsManager.readSettings()).translationEngines.local.selectedGroupId;
|
|
6766
7366
|
if (!effectiveSelectedGroupId) return void 0;
|
|
6767
|
-
return (await this.getModelDownloadPlan({
|
|
7367
|
+
return selectLocalPlanGroup(await this.getModelDownloadPlan({
|
|
6768
7368
|
engineId: "local",
|
|
6769
7369
|
model,
|
|
6770
7370
|
selectedGroupId: effectiveSelectedGroupId
|
|
6771
|
-
})
|
|
7371
|
+
}), effectiveSelectedGroupId)?.dtype;
|
|
6772
7372
|
}
|
|
6773
7373
|
async assertLocalModelReady(model, selectedGroupId) {
|
|
6774
7374
|
const plan = await this.getModelDownloadPlan({
|
|
@@ -6776,89 +7376,57 @@ var TranslationEngineService = class {
|
|
|
6776
7376
|
model,
|
|
6777
7377
|
selectedGroupId
|
|
6778
7378
|
});
|
|
6779
|
-
const
|
|
6780
|
-
if (!plan || files.length === 0) throw new Error("No local runtime file plan is available for the selected model.");
|
|
6781
|
-
const
|
|
6782
|
-
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
});
|
|
6786
|
-
if (cacheStatus.allCached) {
|
|
6787
|
-
const current = (await this.localAssetStore.readMap()).get(model);
|
|
6788
|
-
if (current) await this.localAssetStore.upsert({
|
|
6789
|
-
...current,
|
|
6790
|
-
status: "downloaded",
|
|
6791
|
-
progress: 1,
|
|
6792
|
-
bytesDownloaded: plan.estimatedTotalBytes ?? current.bytesDownloaded,
|
|
6793
|
-
totalBytes: plan.estimatedTotalBytes ?? current.totalBytes,
|
|
6794
|
-
resumable: false,
|
|
6795
|
-
error: void 0,
|
|
6796
|
-
plan,
|
|
6797
|
-
files: files.map((file) => ({
|
|
6798
|
-
path: file.path,
|
|
6799
|
-
sizeBytes: file.sizeBytes,
|
|
6800
|
-
downloadedBytes: file.sizeBytes
|
|
6801
|
-
})),
|
|
6802
|
-
installedAt: current.installedAt ?? this.now(),
|
|
6803
|
-
updatedAt: this.now()
|
|
6804
|
-
});
|
|
6805
|
-
return;
|
|
7379
|
+
const selectedGroup = selectLocalPlanGroup(plan, selectedGroupId);
|
|
7380
|
+
if (!plan || !selectedGroup || selectedGroup.files.length === 0) throw new Error("No local runtime file plan is available for the selected model.");
|
|
7381
|
+
const files = selectedGroup.files;
|
|
7382
|
+
const selectedGroupState = await this.readSelectedLocalGroupState(model, selectedGroup.id);
|
|
7383
|
+
if (selectedGroupState?.status === "downloaded" && selectedGroup.rootDir) {
|
|
7384
|
+
if ((await readMissingLocalGroupFiles(selectedGroup.rootDir, files)).length === 0) return;
|
|
6806
7385
|
}
|
|
6807
|
-
|
|
7386
|
+
if (selectedGroupState?.status === "downloaded") return;
|
|
7387
|
+
const allMissingFiles = selectedGroup.rootDir ? await readMissingLocalGroupFiles(selectedGroup.rootDir, files) : files.map((file) => file.path);
|
|
6808
7388
|
const missingFiles = allMissingFiles.slice(0, 3);
|
|
6809
7389
|
const suffix = allMissingFiles.length > missingFiles.length ? ` and ${allMissingFiles.length - missingFiles.length} more` : "";
|
|
6810
7390
|
throw new Error(`Selected local model files are not installed locally: ${missingFiles.join(", ")}${suffix}.`);
|
|
6811
7391
|
}
|
|
6812
|
-
async readLocalRuntimeConfig(model) {
|
|
7392
|
+
async readLocalRuntimeConfig(model, selectedGroupId) {
|
|
7393
|
+
const selectedGroup = selectLocalPlanGroup(await this.getModelDownloadPlan({
|
|
7394
|
+
engineId: "local",
|
|
7395
|
+
model,
|
|
7396
|
+
selectedGroupId
|
|
7397
|
+
}), selectedGroupId);
|
|
7398
|
+
const configPath = selectedGroup?.rootDir ? join(selectedGroup.rootDir, "config.json") : join(this.localCacheDir, "models", model, "config.json");
|
|
6813
7399
|
try {
|
|
6814
|
-
return JSON.parse(await readFile(
|
|
7400
|
+
return JSON.parse(await readFile(configPath, "utf8"));
|
|
6815
7401
|
} catch {
|
|
6816
7402
|
return;
|
|
6817
7403
|
}
|
|
6818
7404
|
}
|
|
6819
|
-
async
|
|
6820
|
-
|
|
6821
|
-
|
|
7405
|
+
async readSelectedLocalGroupState(model, selectedGroupId) {
|
|
7406
|
+
return (await this.localAssetStore.readMap()).get(model)?.groupsState[selectedGroupId];
|
|
7407
|
+
}
|
|
7408
|
+
async loadFactory(engineId, model, settingsSnapshot) {
|
|
7409
|
+
const globalSettings = settingsSnapshot ?? await this.globalSettingsManager.readSettings();
|
|
7410
|
+
if (engineId === "local") return (await import("./src-DPZ2-0Yn.mjs")).createLocalTranslatorFactory({
|
|
6822
7411
|
defaultModel: model ?? globalSettings.translationEngines.local.model,
|
|
6823
7412
|
cacheDir: this.localCacheDir,
|
|
6824
7413
|
localOnly: true
|
|
6825
7414
|
});
|
|
6826
|
-
return (await import("./src-
|
|
7415
|
+
return (await import("./src-CoUhFB25.mjs")).createOpenAICompletionTranslatorFactory({
|
|
6827
7416
|
baseUrl: globalSettings.translationEngines.openai.baseUrl,
|
|
6828
7417
|
token: globalSettings.translationEngines.openai.token,
|
|
6829
7418
|
model: model ?? globalSettings.translationEngines.openai.model
|
|
6830
7419
|
});
|
|
6831
7420
|
}
|
|
6832
|
-
async loadLocalTransformersModuleForPlan(_projectDir, _globalSettingsManager) {
|
|
6833
|
-
return await import("@huggingface/transformers");
|
|
6834
|
-
}
|
|
6835
7421
|
};
|
|
6836
|
-
function
|
|
6837
|
-
if (
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
const matchingAssetGroup = state.plan?.groups?.find((asset) => asset.id === group.id);
|
|
6841
|
-
if (!matchingAssetGroup) return group;
|
|
6842
|
-
return {
|
|
6843
|
-
...group,
|
|
6844
|
-
estimatedTotalBytes: group.estimatedTotalBytes ?? matchingAssetGroup.estimatedTotalBytes,
|
|
6845
|
-
files: group.files.map((file) => {
|
|
6846
|
-
const matchingAssetFile = matchingAssetGroup.files.find((asset) => asset.path === file.path);
|
|
6847
|
-
return matchingAssetFile?.sizeBytes !== void 0 && file.sizeBytes === void 0 ? {
|
|
6848
|
-
...file,
|
|
6849
|
-
sizeBytes: matchingAssetFile.sizeBytes
|
|
6850
|
-
} : file;
|
|
6851
|
-
})
|
|
6852
|
-
};
|
|
6853
|
-
});
|
|
6854
|
-
return {
|
|
6855
|
-
...plan,
|
|
6856
|
-
estimatedTotalBytes: plan.estimatedTotalBytes ?? assetGroup?.estimatedTotalBytes ?? state.plan.estimatedTotalBytes,
|
|
6857
|
-
groups: mergedGroups
|
|
6858
|
-
};
|
|
7422
|
+
function resolveBatchTranslateModel(input, settings) {
|
|
7423
|
+
if (input.model) return input.model;
|
|
7424
|
+
if (input.engineId === "local") return settings.translationEngines.local.model;
|
|
7425
|
+
if (input.engineId === "openai") return settings.translationEngines.openai.model;
|
|
6859
7426
|
}
|
|
6860
7427
|
function selectPersistedLocalPlan(state, selectedGroupId) {
|
|
6861
|
-
|
|
7428
|
+
if (!state) return null;
|
|
7429
|
+
const plan = LocalModelAssetStateSchema.parse(state).plan;
|
|
6862
7430
|
if (!plan) return null;
|
|
6863
7431
|
if (!selectedGroupId || !plan.groups?.length) return {
|
|
6864
7432
|
...plan,
|
|
@@ -6868,7 +7436,7 @@ function selectPersistedLocalPlan(state, selectedGroupId) {
|
|
|
6868
7436
|
files: [...group.files]
|
|
6869
7437
|
}))
|
|
6870
7438
|
};
|
|
6871
|
-
const selectedGroup = plan
|
|
7439
|
+
const selectedGroup = selectLocalPlanGroup(plan, selectedGroupId);
|
|
6872
7440
|
if (!selectedGroup) return null;
|
|
6873
7441
|
return {
|
|
6874
7442
|
modelId: plan.modelId,
|
|
@@ -6882,6 +7450,22 @@ function selectPersistedLocalPlan(state, selectedGroupId) {
|
|
|
6882
7450
|
}))
|
|
6883
7451
|
};
|
|
6884
7452
|
}
|
|
7453
|
+
function selectLocalPlanGroup(plan, selectedGroupId) {
|
|
7454
|
+
if (!plan?.groups?.length) return void 0;
|
|
7455
|
+
const requestedGroupId = selectedGroupId ?? plan.selectedGroupId;
|
|
7456
|
+
return plan.groups.find((group) => group.id === requestedGroupId) ?? plan.groups.find((group) => group.baseGroupId === requestedGroupId) ?? plan.groups.find((group) => group.selected) ?? plan.groups[0];
|
|
7457
|
+
}
|
|
7458
|
+
async function readMissingLocalGroupFiles(rootDir, files) {
|
|
7459
|
+
return (await Promise.all(files.map(async (file) => {
|
|
7460
|
+
try {
|
|
7461
|
+
const entry = await stat(join(rootDir, file.path));
|
|
7462
|
+
if (file.sizeBytes !== void 0 && entry.size < file.sizeBytes) return file.path;
|
|
7463
|
+
return null;
|
|
7464
|
+
} catch {
|
|
7465
|
+
return file.path;
|
|
7466
|
+
}
|
|
7467
|
+
}))).filter((file) => file !== null);
|
|
7468
|
+
}
|
|
6885
7469
|
|
|
6886
7470
|
//#endregion
|
|
6887
7471
|
//#region src/workflow-invocation-service.ts
|
|
@@ -7178,6 +7762,7 @@ function createServer(config) {
|
|
|
7178
7762
|
});
|
|
7179
7763
|
const nmtModelCacheDir = config.runtimePaths?.localModelCacheDir ?? getDefaultLocalModelCacheDir();
|
|
7180
7764
|
const nmtModelIndexPath = config.runtimePaths?.localModelAssetIndexPath ?? getDefaultLocalModelIndexPath();
|
|
7765
|
+
const nmtModelProfileManifestPath = config.runtimePaths?.localModelProfileManifestPath ?? getDefaultLocalModelProfileManifestPath();
|
|
7181
7766
|
const nmtModelFetchCachePath = config.runtimePaths?.localModelFetchCachePath ?? getDefaultLocalModelFetchCachePath();
|
|
7182
7767
|
const translationEngineService = new TranslationEngineService({
|
|
7183
7768
|
projectDir: config.projectDir,
|
|
@@ -7193,6 +7778,7 @@ function createServer(config) {
|
|
|
7193
7778
|
globalSettingsManager,
|
|
7194
7779
|
cacheDir: nmtModelCacheDir,
|
|
7195
7780
|
indexPath: nmtModelIndexPath,
|
|
7781
|
+
profileManifestPath: nmtModelProfileManifestPath,
|
|
7196
7782
|
fetchCachePath: nmtModelFetchCachePath
|
|
7197
7783
|
});
|
|
7198
7784
|
const watcher = config.enableWatcher !== false ? new OpenSpecWatcher(config.projectDir) : void 0;
|