Skip to main content
Glama
model-downloader.ts4.04 kB
import { mkdir, unlink } from "node:fs/promises"; import { createWriteStream } from "node:fs"; import { existsSync } from "node:fs"; import { dirname } from "node:path"; import { Readable } from "node:stream"; import { MODEL_PATH } from "../config.js"; const MODEL_REPO = "fulgidus/zignet-qwen2.5-coder-7b"; const MODEL_FILE = "gguf/zignet-qwen-7b-q4km.gguf"; const MODEL_SIZE_MB = 4400; // 4.4GB export interface DownloadProgress { downloaded: number; total: number; percent: number; } export class ModelDownloader { private readonly modelPath: string; constructor() { this.modelPath = MODEL_PATH; } /** * Get the path to the model file */ getModelPath(): string { return this.modelPath; } /** * Check if model is already downloaded */ isModelAvailable(): boolean { return existsSync(this.modelPath); } /** * Download the GGUF model from HuggingFace */ async downloadModel( onProgress?: (progress: DownloadProgress) => void, ): Promise<void> { if (this.isModelAvailable()) { console.log("✅ Model already downloaded:", this.modelPath); return; } console.log("📥 Downloading ZigNet model from HuggingFace..."); console.log(`📦 Size: ${MODEL_SIZE_MB}MB`); console.log(`📍 Repo: ${MODEL_REPO}`); // Ensure models directory exists const modelsDir = dirname(this.modelPath); await mkdir(modelsDir, { recursive: true }); // HuggingFace CDN URL const url = `https://huggingface.co/${MODEL_REPO}/resolve/main/${MODEL_FILE}`; try { const response = await fetch(url); if (!response.ok) { throw new Error( `Failed to download model: ${response.statusText}`, ); } const totalBytes = parseInt( response.headers.get("content-length") || "0", 10, ); if (!response.body) { throw new Error("Response body is null"); } const fileStream = createWriteStream(this.modelPath); let downloadedBytes = 0; // Convert Web ReadableStream to Node Readable const nodeStream = Readable.fromWeb( response.body as ReadableStream<Uint8Array>, ); // Track progress nodeStream.on("data", (chunk: Buffer) => { downloadedBytes += chunk.length; if (onProgress && totalBytes > 0) { onProgress({ downloaded: downloadedBytes, total: totalBytes, percent: (downloadedBytes / totalBytes) * 100, }); } }); // Pipe to file await new Promise<void>((resolve, reject) => { nodeStream.pipe(fileStream); nodeStream.on("error", reject); fileStream.on("error", reject); fileStream.on("finish", resolve); }); console.log("✅ Model downloaded successfully!"); console.log(`📁 Location: ${this.modelPath}`); } catch (error) { // Clean up partial download if (existsSync(this.modelPath)) { await unlink(this.modelPath); } throw new Error( `Failed to download model: ${error instanceof Error ? error.message : String(error)}`, ); } } /** * Ensure model is downloaded, download if needed */ async ensureModel( onProgress?: (progress: DownloadProgress) => void, ): Promise<string> { if (!this.isModelAvailable()) { await this.downloadModel(onProgress); } return this.modelPath; } } /** * Singleton instance */ export const modelDownloader = new ModelDownloader();

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/fulgidus/zignet'

If you have feedback or need assistance with the MCP directory API, please join our Discord server