unsloth/studio/frontend/src/features/export/api/export-api.ts
Roland Tannous ebe45981dd
feat: support GGUF export for non-PEFT models + fix venv_t5 switching for local checkpoints (#4455)
* feat: support full model GGUF export, disable incompatible methods in UI

* fix: resolve base model from config.json for venv_t5 export switching

* feat: detect BNB-quantized models and disable all export methods for quantized non-PEFT checkpoints

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix: relocate Ollama Modelfile alongside GGUFs during non-PEFT export cleanup

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-03-20 12:13:18 +04:00

133 lines
3.9 KiB
TypeScript

// SPDX-License-Identifier: AGPL-3.0-only
// Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
import { authFetch } from "@/features/auth";
async function readError(response: Response): Promise<string> {
try {
const payload = (await response.json()) as { detail?: string; message?: string };
return payload.detail || payload.message || `Request failed (${response.status})`;
} catch {
return `Request failed (${response.status})`;
}
}
async function parseJson<T>(response: Response): Promise<T> {
if (!response.ok) {
throw new Error(await readError(response));
}
return (await response.json()) as T;
}
export interface CheckpointInfo {
display_name: string;
path: string;
loss?: number | null;
}
export interface ModelCheckpoints {
name: string;
checkpoints: CheckpointInfo[];
base_model?: string | null;
peft_type?: string | null;
lora_rank?: number | null;
is_quantized?: boolean;
}
export interface CheckpointListResponse {
outputs_dir: string;
models: ModelCheckpoints[];
}
export interface ExportOperationResponse {
success: boolean;
message: string;
details?: Record<string, unknown> | null;
}
export async function fetchCheckpoints(): Promise<CheckpointListResponse> {
const response = await authFetch("/api/models/checkpoints");
return parseJson<CheckpointListResponse>(response);
}
export async function loadCheckpoint(params: {
checkpoint_path: string;
max_seq_length?: number;
load_in_4bit?: boolean;
/** Allow loading models with custom code. Only enable for checkpoints you trust. */
trust_remote_code?: boolean;
}): Promise<ExportOperationResponse> {
const response = await authFetch("/api/export/load-checkpoint", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(params),
});
return parseJson<ExportOperationResponse>(response);
}
export async function exportMerged(params: {
save_directory: string;
format_type?: string;
push_to_hub?: boolean;
repo_id?: string | null;
hf_token?: string | null;
private?: boolean;
}): Promise<ExportOperationResponse> {
const response = await authFetch("/api/export/export/merged", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(params),
});
return parseJson<ExportOperationResponse>(response);
}
export async function exportBase(params: {
save_directory: string;
push_to_hub?: boolean;
repo_id?: string | null;
hf_token?: string | null;
private?: boolean;
base_model_id?: string | null;
}): Promise<ExportOperationResponse> {
const response = await authFetch("/api/export/export/base", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(params),
});
return parseJson<ExportOperationResponse>(response);
}
export async function exportGGUF(params: {
save_directory: string;
quantization_method: string;
push_to_hub?: boolean;
repo_id?: string | null;
hf_token?: string | null;
}): Promise<ExportOperationResponse> {
const response = await authFetch("/api/export/export/gguf", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(params),
});
return parseJson<ExportOperationResponse>(response);
}
export async function exportLoRA(params: {
save_directory: string;
push_to_hub?: boolean;
repo_id?: string | null;
hf_token?: string | null;
private?: boolean;
}): Promise<ExportOperationResponse> {
const response = await authFetch("/api/export/export/lora", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(params),
});
return parseJson<ExportOperationResponse>(response);
}
export async function cleanupExport(): Promise<ExportOperationResponse> {
const response = await authFetch("/api/export/cleanup", { method: "POST" });
return parseJson<ExportOperationResponse>(response);
}