Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'
This commit is contained in:
323
vendor/ruvector/npm/packages/ruvllm/src/models.js
vendored
Normal file
323
vendor/ruvector/npm/packages/ruvllm/src/models.js
vendored
Normal file
@@ -0,0 +1,323 @@
|
||||
"use strict";
|
||||
/**
|
||||
* RuvLTRA Model Registry and Downloader
|
||||
*
|
||||
* Automatically downloads GGUF models from HuggingFace Hub.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { ModelDownloader, RUVLTRA_MODELS } from '@ruvector/ruvllm';
|
||||
*
|
||||
* // Download the Claude Code optimized model
|
||||
* const downloader = new ModelDownloader();
|
||||
* const modelPath = await downloader.download('claude-code');
|
||||
*
|
||||
* // Or download all models
|
||||
* await downloader.downloadAll();
|
||||
* ```
|
||||
*/
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ModelDownloader = exports.MODEL_ALIASES = exports.RUVLTRA_MODELS = void 0;
|
||||
exports.getDefaultModelsDir = getDefaultModelsDir;
|
||||
exports.resolveModelId = resolveModelId;
|
||||
exports.getModelInfo = getModelInfo;
|
||||
exports.listModels = listModels;
|
||||
const fs_1 = require("fs");
|
||||
const path_1 = require("path");
|
||||
const os_1 = require("os");
|
||||
/** HuggingFace repository */
|
||||
const HF_REPO = 'ruv/ruvltra';
|
||||
const HF_BASE_URL = `https://huggingface.co/${HF_REPO}/resolve/main`;
|
||||
/** Available RuvLTRA models */
|
||||
exports.RUVLTRA_MODELS = {
|
||||
'claude-code': {
|
||||
id: 'claude-code',
|
||||
name: 'RuvLTRA Claude Code',
|
||||
filename: 'ruvltra-claude-code-0.5b-q4_k_m.gguf',
|
||||
sizeBytes: 398000000,
|
||||
size: '398 MB',
|
||||
parameters: '0.5B',
|
||||
useCase: 'Claude Code workflows, agentic coding',
|
||||
quantization: 'Q4_K_M',
|
||||
contextLength: 4096,
|
||||
url: `${HF_BASE_URL}/ruvltra-claude-code-0.5b-q4_k_m.gguf`,
|
||||
},
|
||||
'small': {
|
||||
id: 'small',
|
||||
name: 'RuvLTRA Small',
|
||||
filename: 'ruvltra-small-0.5b-q4_k_m.gguf',
|
||||
sizeBytes: 398000000,
|
||||
size: '398 MB',
|
||||
parameters: '0.5B',
|
||||
useCase: 'Edge devices, IoT, resource-constrained environments',
|
||||
quantization: 'Q4_K_M',
|
||||
contextLength: 4096,
|
||||
url: `${HF_BASE_URL}/ruvltra-small-0.5b-q4_k_m.gguf`,
|
||||
},
|
||||
'medium': {
|
||||
id: 'medium',
|
||||
name: 'RuvLTRA Medium',
|
||||
filename: 'ruvltra-medium-1.1b-q4_k_m.gguf',
|
||||
sizeBytes: 669000000,
|
||||
size: '669 MB',
|
||||
parameters: '1.1B',
|
||||
useCase: 'General purpose, balanced performance',
|
||||
quantization: 'Q4_K_M',
|
||||
contextLength: 8192,
|
||||
url: `${HF_BASE_URL}/ruvltra-medium-1.1b-q4_k_m.gguf`,
|
||||
},
|
||||
};
|
||||
/** Model aliases for convenience */
|
||||
exports.MODEL_ALIASES = {
|
||||
'cc': 'claude-code',
|
||||
'claudecode': 'claude-code',
|
||||
'claude': 'claude-code',
|
||||
's': 'small',
|
||||
'sm': 'small',
|
||||
'm': 'medium',
|
||||
'med': 'medium',
|
||||
'default': 'claude-code',
|
||||
};
|
||||
/**
|
||||
* Get the default models directory
|
||||
*/
|
||||
function getDefaultModelsDir() {
|
||||
return (0, path_1.join)((0, os_1.homedir)(), '.ruvllm', 'models');
|
||||
}
|
||||
/**
|
||||
* Resolve model ID from alias or direct ID
|
||||
*/
|
||||
function resolveModelId(modelIdOrAlias) {
|
||||
const normalized = modelIdOrAlias.toLowerCase().trim();
|
||||
// Direct match
|
||||
if (exports.RUVLTRA_MODELS[normalized]) {
|
||||
return normalized;
|
||||
}
|
||||
// Alias match
|
||||
if (exports.MODEL_ALIASES[normalized]) {
|
||||
return exports.MODEL_ALIASES[normalized];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Get model info by ID or alias
|
||||
*/
|
||||
function getModelInfo(modelIdOrAlias) {
|
||||
const id = resolveModelId(modelIdOrAlias);
|
||||
return id ? exports.RUVLTRA_MODELS[id] : null;
|
||||
}
|
||||
/**
|
||||
* List all available models
|
||||
*/
|
||||
function listModels() {
|
||||
return Object.values(exports.RUVLTRA_MODELS);
|
||||
}
|
||||
/**
|
||||
* Model downloader for RuvLTRA GGUF models
|
||||
*/
|
||||
class ModelDownloader {
|
||||
constructor(modelsDir) {
|
||||
this.modelsDir = modelsDir || getDefaultModelsDir();
|
||||
}
|
||||
/**
|
||||
* Get the path where a model would be saved
|
||||
*/
|
||||
getModelPath(modelIdOrAlias) {
|
||||
const model = getModelInfo(modelIdOrAlias);
|
||||
if (!model)
|
||||
return null;
|
||||
return (0, path_1.join)(this.modelsDir, model.filename);
|
||||
}
|
||||
/**
|
||||
* Check if a model is already downloaded
|
||||
*/
|
||||
isDownloaded(modelIdOrAlias) {
|
||||
const path = this.getModelPath(modelIdOrAlias);
|
||||
if (!path)
|
||||
return false;
|
||||
if (!(0, fs_1.existsSync)(path))
|
||||
return false;
|
||||
// Verify size matches expected
|
||||
const model = getModelInfo(modelIdOrAlias);
|
||||
if (!model)
|
||||
return false;
|
||||
const stats = (0, fs_1.statSync)(path);
|
||||
// Allow 5% variance for size check
|
||||
const minSize = model.sizeBytes * 0.95;
|
||||
return stats.size >= minSize;
|
||||
}
|
||||
/**
|
||||
* Get download status for all models
|
||||
*/
|
||||
getStatus() {
|
||||
return listModels().map(model => ({
|
||||
model,
|
||||
downloaded: this.isDownloaded(model.id),
|
||||
path: this.getModelPath(model.id),
|
||||
}));
|
||||
}
|
||||
/**
|
||||
* Download a model from HuggingFace
|
||||
*/
|
||||
async download(modelIdOrAlias, options = {}) {
|
||||
const model = getModelInfo(modelIdOrAlias);
|
||||
if (!model) {
|
||||
const available = listModels().map(m => m.id).join(', ');
|
||||
throw new Error(`Unknown model: ${modelIdOrAlias}. Available models: ${available}`);
|
||||
}
|
||||
const destDir = options.modelsDir || this.modelsDir;
|
||||
const destPath = (0, path_1.join)(destDir, model.filename);
|
||||
// Check if already downloaded
|
||||
if (!options.force && this.isDownloaded(model.id)) {
|
||||
return destPath;
|
||||
}
|
||||
// Ensure directory exists
|
||||
if (!(0, fs_1.existsSync)(destDir)) {
|
||||
(0, fs_1.mkdirSync)(destDir, { recursive: true });
|
||||
}
|
||||
// Download with progress tracking
|
||||
const tempPath = `${destPath}.tmp`;
|
||||
let startTime = Date.now();
|
||||
let lastProgressTime = startTime;
|
||||
let lastDownloaded = 0;
|
||||
try {
|
||||
// Use dynamic import for node-fetch if native fetch not available
|
||||
const fetchFn = globalThis.fetch || (await Promise.resolve().then(() => __importStar(require('node:https')))).default;
|
||||
const response = await fetch(model.url, {
|
||||
headers: {
|
||||
'User-Agent': 'RuvLLM/2.3.0',
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
const contentLength = parseInt(response.headers.get('content-length') || String(model.sizeBytes));
|
||||
// Create write stream
|
||||
const fileStream = (0, fs_1.createWriteStream)(tempPath);
|
||||
let downloaded = 0;
|
||||
// Stream with progress
|
||||
const reader = response.body?.getReader();
|
||||
if (!reader) {
|
||||
throw new Error('Response body is not readable');
|
||||
}
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done)
|
||||
break;
|
||||
downloaded += value.length;
|
||||
fileStream.write(value);
|
||||
// Report progress
|
||||
if (options.onProgress) {
|
||||
const now = Date.now();
|
||||
const elapsed = (now - lastProgressTime) / 1000;
|
||||
const bytesThisInterval = downloaded - lastDownloaded;
|
||||
const speedBps = elapsed > 0 ? bytesThisInterval / elapsed : 0;
|
||||
const remaining = contentLength - downloaded;
|
||||
const etaSeconds = speedBps > 0 ? remaining / speedBps : 0;
|
||||
options.onProgress({
|
||||
modelId: model.id,
|
||||
downloaded,
|
||||
total: contentLength,
|
||||
percent: Math.round((downloaded / contentLength) * 100),
|
||||
speedBps,
|
||||
etaSeconds,
|
||||
});
|
||||
lastProgressTime = now;
|
||||
lastDownloaded = downloaded;
|
||||
}
|
||||
}
|
||||
fileStream.end();
|
||||
// Wait for file to be fully written
|
||||
await new Promise((resolve, reject) => {
|
||||
fileStream.on('finish', resolve);
|
||||
fileStream.on('error', reject);
|
||||
});
|
||||
// Move temp file to final destination
|
||||
if ((0, fs_1.existsSync)(destPath)) {
|
||||
(0, fs_1.unlinkSync)(destPath);
|
||||
}
|
||||
(0, fs_1.renameSync)(tempPath, destPath);
|
||||
return destPath;
|
||||
}
|
||||
catch (error) {
|
||||
// Clean up temp file on error
|
||||
if ((0, fs_1.existsSync)(tempPath)) {
|
||||
try {
|
||||
(0, fs_1.unlinkSync)(tempPath);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Download all available models
|
||||
*/
|
||||
async downloadAll(options = {}) {
|
||||
const paths = [];
|
||||
for (const model of listModels()) {
|
||||
const path = await this.download(model.id, options);
|
||||
paths.push(path);
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
/**
|
||||
* Delete a downloaded model
|
||||
*/
|
||||
delete(modelIdOrAlias) {
|
||||
const path = this.getModelPath(modelIdOrAlias);
|
||||
if (!path || !(0, fs_1.existsSync)(path)) {
|
||||
return false;
|
||||
}
|
||||
(0, fs_1.unlinkSync)(path);
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Delete all downloaded models
|
||||
*/
|
||||
deleteAll() {
|
||||
let count = 0;
|
||||
for (const model of listModels()) {
|
||||
if (this.delete(model.id)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
exports.ModelDownloader = ModelDownloader;
|
||||
exports.default = ModelDownloader;
|
||||
//# sourceMappingURL=models.js.map
|
||||
Reference in New Issue
Block a user