diff --git a/src/lib/apis/ollama/index.ts b/src/lib/apis/ollama/index.ts index 5b0f9870..9d3ac0a8 100644 --- a/src/lib/apis/ollama/index.ts +++ b/src/lib/apis/ollama/index.ts @@ -249,7 +249,8 @@ export const deleteModel = async (token: string, tagName: string) => { }; export const pullModel = async (token: string, tagName: string) => { -try { + let error = null; + const res = await fetch(`${OLLAMA_API_BASE_URL}/pull`, { method: 'POST', headers: { @@ -259,9 +260,31 @@ try { body: JSON.stringify({ name: tagName }) - }) + }).catch((err) => { + console.log(err); + error = err; + + if ('detail' in err) { + error = err.detail; + } + + return null; + }); + if (error) { + throw error; + } return res; -} catch (error) { - throw error; -} }; + +// export const pullModel = async (token: string, tagName: string) => { +// return await fetch(`${OLLAMA_API_BASE_URL}/pull`, { +// method: 'POST', +// headers: { +// 'Content-Type': 'text/event-stream', +// Authorization: `Bearer ${token}` +// }, +// body: JSON.stringify({ +// name: tagName +// }) +// }); +// }; diff --git a/src/lib/components/chat/SettingsModal.svelte b/src/lib/components/chat/SettingsModal.svelte index 4ef2f016..e0f04d82 100644 --- a/src/lib/components/chat/SettingsModal.svelte +++ b/src/lib/components/chat/SettingsModal.svelte @@ -51,7 +51,11 @@ let notificationEnabled = false; let system = ''; const MAX_PARALLEL_DOWNLOADS = 3; - const modelDownloadQueue = queue((task:{modelName: string}, cb) => pullModelHandlerProcessor({modelName: task.modelName, callback: cb}), MAX_PARALLEL_DOWNLOADS); + const modelDownloadQueue = queue( + (task: { modelName: string }, cb) => + pullModelHandlerProcessor({ modelName: task.modelName, callback: cb }), + MAX_PARALLEL_DOWNLOADS + ); let modelDownloadStatus: Record = {}; // Advanced @@ -250,11 +254,13 @@ saveSettings({ saveChatHistory: saveChatHistory }); }; - const pullModelHandlerProcessor = async (opts:{modelName:string, callback: Function}) => { + const pullModelHandlerProcessor = async (opts: { modelName: string; callback: Function }) => { + const res = await pullModel(localStorage.token, opts.modelName).catch((error) => { + opts.callback({ success: false, error, modelName: opts.modelName }); + return null; + }); - try { - const res = await pullModel(localStorage.token, opts.modelName); - + if (res) { const reader = res.body .pipeThrough(new TextDecoderStream()) .pipeThrough(splitStream('\n')) @@ -270,102 +276,70 @@ for (const line of lines) { if (line !== '') { let data = JSON.parse(line); - if (data.error) { - throw data.error; - } - if (data.detail) { - throw data.detail; - } - if (data.status) { - if (data.digest) { - let downloadProgress = 0; - if (data.completed) { - downloadProgress = Math.round((data.completed / data.total) * 1000) / 10; - } else { - downloadProgress = 100; - } - modelDownloadStatus[opts.modelName] = {pullProgress: downloadProgress, digest: data.digest}; + if (data.error) { + throw data.error; + } + if (data.detail) { + throw data.detail; + } + if (data.status) { + if (data.digest) { + let downloadProgress = 0; + if (data.completed) { + downloadProgress = Math.round((data.completed / data.total) * 1000) / 10; + } else { + downloadProgress = 100; + } + modelDownloadStatus[opts.modelName] = { + pullProgress: downloadProgress, + digest: data.digest + }; + } } - } } } } catch (error) { - console.log('Failed to read from data stream', error); - throw error; + console.log(error); + opts.callback({ success: false, error, modelName: opts.modelName }); } } - opts.callback({success: true, modelName: opts.modelName}); - } catch (error) { - console.error(error); - opts.callback({success:false, error, modelName: opts.modelName}); + opts.callback({ success: true, modelName: opts.modelName }); } + }; - - }; - - const pullModelHandler = async() => { - if(modelDownloadStatus[modelTag]){ - toast.error("Model already in queue for downloading."); + const pullModelHandler = async () => { + if (modelDownloadStatus[modelTag]) { + toast.error('Model already in queue for downloading.'); return; } - if(Object.keys(modelDownloadStatus).length === 3){ + if (Object.keys(modelDownloadStatus).length === 3) { toast.error('Maximum of 3 models can be downloading simultaneously. Please try again later'); return; } + modelTransferring = true; - modelDownloadQueue.push({modelName: modelTag},async (data:{modelName: string; success: boolean; error?: Error}) => { - const {modelName} = data; - // Remove the downloaded model - delete modelDownloadStatus[modelName]; + modelDownloadQueue.push( + { modelName: modelTag }, + async (data: { modelName: string; success: boolean; error?: Error }) => { + const { modelName } = data; + // Remove the downloaded model + delete modelDownloadStatus[modelName]; - if(!data.success){ - toast.error(`There was some issue in downloading the model ${modelName}`); - return; + console.log(data); + + if (!data.success) { + toast.error(data.error); + return; + } + + toast.success(`Model ${modelName} was successfully downloaded`); + models.set(await getModels()); } - - toast.success(`Model ${modelName} was successfully downloaded`); - models.set(await getModels()); - }); + ); modelTag = ''; - modelTransferring = false; - } - - - const calculateSHA256 = async (file) => { - console.log(file); - // Create a FileReader to read the file asynchronously - const reader = new FileReader(); - - // Define a promise to handle the file reading - const readFile = new Promise((resolve, reject) => { - reader.onload = () => resolve(reader.result); - reader.onerror = reject; - }); - - // Read the file as an ArrayBuffer - reader.readAsArrayBuffer(file); - - try { - // Wait for the FileReader to finish reading the file - const buffer = await readFile; - - // Convert the ArrayBuffer to a Uint8Array - const uint8Array = new Uint8Array(buffer); - - // Calculate the SHA-256 hash using Web Crypto API - const hashBuffer = await crypto.subtle.digest('SHA-256', uint8Array); - - // Convert the hash to a hexadecimal string - const hashArray = Array.from(new Uint8Array(hashBuffer)); - const hashHex = hashArray.map((byte) => byte.toString(16).padStart(2, '0')).join(''); - - return `sha256:${hashHex}`; - } catch (error) { - console.error('Error calculating SHA-256 hash:', error); - throw error; - } + modelTransferring = false; }; const uploadModelHandler = async () => { @@ -1190,35 +1164,23 @@ {#if Object.keys(modelDownloadStatus).length > 0} - - - - - - - - - {#each Object.entries(modelDownloadStatus) as [modelName, payload]} - - - - - {/each} - -
Model Name Download progress
{modelName} + {#each Object.entries(modelDownloadStatus) as [modelName, payload]} +
+
{modelName}
+
- { payload.pullProgress ?? 0}% + class="dark:bg-gray-600 bg-gray-500 text-xs font-medium text-gray-100 text-center p-0.5 leading-none rounded-full" + style="width: {Math.max(15, payload.pullProgress ?? 0)}%" + > + {payload.pullProgress ?? 0}% +
+
+ {payload.digest} +
-
- {payload.digest} -
-
- {/if} + + {/each} + {/if}
diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index 2029604e..d9f6fd7d 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -127,3 +127,38 @@ export const findWordIndices = (text) => { return matches; }; + +export const calculateSHA256 = async (file) => { + console.log(file); + // Create a FileReader to read the file asynchronously + const reader = new FileReader(); + + // Define a promise to handle the file reading + const readFile = new Promise((resolve, reject) => { + reader.onload = () => resolve(reader.result); + reader.onerror = reject; + }); + + // Read the file as an ArrayBuffer + reader.readAsArrayBuffer(file); + + try { + // Wait for the FileReader to finish reading the file + const buffer = await readFile; + + // Convert the ArrayBuffer to a Uint8Array + const uint8Array = new Uint8Array(buffer); + + // Calculate the SHA-256 hash using Web Crypto API + const hashBuffer = await crypto.subtle.digest('SHA-256', uint8Array); + + // Convert the hash to a hexadecimal string + const hashArray = Array.from(new Uint8Array(hashBuffer)); + const hashHex = hashArray.map((byte) => byte.toString(16).padStart(2, '0')).join(''); + + return `sha256:${hashHex}`; + } catch (error) { + console.error('Error calculating SHA-256 hash:', error); + throw error; + } +};