feat: Tier-1 Model Manager with Hot-Swapping, HF Download Engine, bug fixes, and test repairs
- Phase 4: Hugging Face Download Engine — streaming GGUF downloads with progress, cancellation, and managed-model storage - Phase 5: Model Source Validation — source-mode compatibility, path/file validation, inline error UI with recovery guidance - Hot-swap: stop runtime → update model_path → restart atomic command - Model library: folder open, delete model file, local model management - SettingsPage unified hub: local models list + HF search with 3-state action buttons - Bug fixes: send button text restored (4 failing tests), reasoning-toggle aria-label, Rust warnings - All 89 tests passing, cargo check clean
This commit is contained in:
1 parent
01b629cb4a
commit
9a093dca15
23 files changed
+1798
-344
No files matched your search
@@ -1,6 +1,7 @@
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { createDefaultRuntimeSnapshot, createRuntimeLifecycleResult } from "./runtime-utils";
|
||||
import type {
|
||||
HuggingFaceDownloadProgress,
|
||||
ModelLibraryState,
|
||||
HuggingFaceModelResult,
|
||||
RuntimeConfig,
|
||||
@@ -11,6 +12,7 @@ import type {
|
||||
} from "./types";
|
||||
|
||||
const STORAGE_KEY = "forgelm_runtime_snapshot_v1";
|
||||
export const HUGGING_FACE_DOWNLOAD_EVENT = "hugging-face-download-progress";
|
||||
|
||||
function canUseLocalStorage(): boolean {
|
||||
return typeof window !== "undefined" && typeof window.localStorage !== "undefined";
|
||||
@@ -146,6 +148,62 @@ export async function searchHuggingFaceModels(query: string, token = ""): Promis
|
||||
});
|
||||
}
|
||||
|
||||
export async function startHuggingFaceDownload(repo: string, fileName: string, token = ""): Promise<string> {
|
||||
try {
|
||||
return await invoke<string>("start_hugging_face_download_command", {
|
||||
repo,
|
||||
fileName,
|
||||
token: token.trim() || null,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("[api] startHuggingFaceDownload failed:", err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export async function cancelHuggingFaceDownload(downloadId: string): Promise<void> {
|
||||
try {
|
||||
return await invoke<void>("cancel_hugging_face_download_command", {
|
||||
downloadId,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("[api] cancelHuggingFaceDownload failed:", err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getModelLibraryState(): Promise<ModelLibraryState> {
|
||||
return invoke<ModelLibraryState>("get_model_library_state_command");
|
||||
}
|
||||
|
||||
/// Hot-swap: stop runtime → update model_path → restart.
|
||||
export async function swapModel(newModelPath: string): Promise<RuntimeSnapshot> {
|
||||
try {
|
||||
return await invoke<RuntimeSnapshot>("swap_model_command", { newModelPath });
|
||||
} catch (err) {
|
||||
console.error("[api] swapModel failed:", err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete a local .gguf model file from disk.
|
||||
export async function deleteModelFile(filePath: string): Promise<void> {
|
||||
try {
|
||||
return await invoke<void>("delete_model_file", { filePath });
|
||||
} catch (err) {
|
||||
console.error("[api] deleteModelFile failed:", err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/// Open the folder containing a model in the native file explorer.
|
||||
export async function openModelFolder(filePath: string): Promise<void> {
|
||||
try {
|
||||
return await invoke<void>("open_model_folder_command", { filePath });
|
||||
} catch (err) {
|
||||
console.error("[api] openModelFolder failed:", err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export type { HuggingFaceDownloadProgress };
|
||||
Reference in new issue
Block a user