chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { homedir } from 'node:os'
|
||||
|
||||
import { FileHelper } from '@/helpers/file-helper'
|
||||
import { NetworkHelper } from '@/helpers/network-helper'
|
||||
|
||||
import { createSetupStatus } from './setup-status'
|
||||
|
||||
const MOVE_FALLBACK_ERROR_CODES = new Set(['EXDEV', 'EPERM', 'EBUSY', 'EACCES'])
|
||||
const QMD_MODELS_DIR_PATH = path.join(homedir(), '.cache', 'qmd', 'models')
|
||||
|
||||
const QMD_MODELS = [
|
||||
{
|
||||
/**
|
||||
* We do not use it yet, but better to get it now,
|
||||
* so it'd be ready when we enable embeddings
|
||||
*/
|
||||
url: 'https://huggingface.co/ggml-org/embeddinggemma-300M-GGUF/resolve/main/embeddinggemma-300M-Q8_0.gguf?download=true',
|
||||
filename: 'hf_ggml-org_embeddinggemma-300M-Q8_0.gguf'
|
||||
},
|
||||
{
|
||||
url: 'https://huggingface.co/ggml-org/Qwen3-Reranker-0.6B-Q8_0-GGUF/resolve/main/qwen3-reranker-0.6b-q8_0.gguf?download=true',
|
||||
filename: 'hf_ggml-org_qwen3-reranker-0.6b-q8_0.gguf'
|
||||
},
|
||||
{
|
||||
url: 'https://huggingface.co/tobil/qmd-query-expansion-1.7B/resolve/main/qmd-query-expansion-1.7B-Q4_K_M.gguf?download=true',
|
||||
filename: 'hf_tobil_qmd-query-expansion-1.7B-q4_k_m.gguf'
|
||||
}
|
||||
]
|
||||
|
||||
function getModelFilenameFromURL(modelURL) {
|
||||
const parsedURL = new URL(modelURL)
|
||||
|
||||
return path.basename(parsedURL.pathname)
|
||||
}
|
||||
|
||||
function isMoveFallbackError(error) {
|
||||
return (
|
||||
error instanceof Error &&
|
||||
'code' in error &&
|
||||
MOVE_FALLBACK_ERROR_CODES.has(error.code)
|
||||
)
|
||||
}
|
||||
|
||||
async function movePath(sourcePath, destinationPath) {
|
||||
try {
|
||||
await fs.promises.rename(sourcePath, destinationPath)
|
||||
} catch (error) {
|
||||
if (!isMoveFallbackError(error)) {
|
||||
throw error
|
||||
}
|
||||
|
||||
await fs.promises.copyFile(sourcePath, destinationPath)
|
||||
await fs.promises.rm(sourcePath, { force: true })
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadModel(model) {
|
||||
const destinationPath = path.join(QMD_MODELS_DIR_PATH, model.filename)
|
||||
const legacyFilename = getModelFilenameFromURL(model.url)
|
||||
const legacyPath = path.join(QMD_MODELS_DIR_PATH, legacyFilename)
|
||||
|
||||
if (fs.existsSync(destinationPath)) {
|
||||
return 'existing'
|
||||
}
|
||||
|
||||
if (legacyFilename !== model.filename && fs.existsSync(legacyPath)) {
|
||||
await movePath(legacyPath, destinationPath)
|
||||
|
||||
return 'renamed'
|
||||
}
|
||||
|
||||
const resolvedURL = await NetworkHelper.setHuggingFaceURL(model.url)
|
||||
|
||||
await FileHelper.downloadFile(resolvedURL, destinationPath)
|
||||
|
||||
return 'downloaded'
|
||||
}
|
||||
|
||||
export default async () => {
|
||||
const status = createSetupStatus('Checking QMD models...').start()
|
||||
|
||||
try {
|
||||
await fs.promises.mkdir(QMD_MODELS_DIR_PATH, {
|
||||
recursive: true
|
||||
})
|
||||
|
||||
status.pause()
|
||||
let downloadedModelCount = 0
|
||||
|
||||
for (const model of QMD_MODELS) {
|
||||
const modelState = await downloadModel(model)
|
||||
|
||||
if (modelState === 'downloaded') {
|
||||
downloadedModelCount += 1
|
||||
}
|
||||
}
|
||||
|
||||
status.text = 'Finalizing QMD models...'
|
||||
status.start()
|
||||
|
||||
status.succeed(
|
||||
downloadedModelCount > 0
|
||||
? `QMD models: ready - ${downloadedModelCount} downloaded`
|
||||
: 'QMD models: ready'
|
||||
)
|
||||
} catch (e) {
|
||||
if (status.isSpinning) {
|
||||
status.fail('Failed to set up QMD models')
|
||||
}
|
||||
throw e
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user