This commit is contained in:
Timothy J. Baek 2024-01-06 13:02:09 -08:00
parent 8f570bc2ee
commit 3853261b40
3 changed files with 134 additions and 114 deletions

View file

@ -249,7 +249,8 @@ export const deleteModel = async (token: string, tagName: string) => {
}; };
export const pullModel = 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`, { const res = await fetch(`${OLLAMA_API_BASE_URL}/pull`, {
method: 'POST', method: 'POST',
headers: { headers: {
@ -259,9 +260,31 @@ try {
body: JSON.stringify({ body: JSON.stringify({
name: tagName name: tagName
}) })
}) }).catch((err) => {
console.log(err);
error = err;
if ('detail' in err) {
error = err.detail;
}
return null;
});
if (error) {
throw error;
}
return res; 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
// })
// });
// };

View file

@ -51,7 +51,11 @@
let notificationEnabled = false; let notificationEnabled = false;
let system = ''; let system = '';
const MAX_PARALLEL_DOWNLOADS = 3; 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<string, any> = {}; let modelDownloadStatus: Record<string, any> = {};
// Advanced // Advanced
@ -250,11 +254,13 @@
saveSettings({ saveChatHistory: saveChatHistory }); 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 { if (res) {
const res = await pullModel(localStorage.token, opts.modelName);
const reader = res.body const reader = res.body
.pipeThrough(new TextDecoderStream()) .pipeThrough(new TextDecoderStream())
.pipeThrough(splitStream('\n')) .pipeThrough(splitStream('\n'))
@ -270,102 +276,70 @@
for (const line of lines) { for (const line of lines) {
if (line !== '') { if (line !== '') {
let data = JSON.parse(line); let data = JSON.parse(line);
if (data.error) { if (data.error) {
throw data.error; throw data.error;
} }
if (data.detail) { if (data.detail) {
throw data.detail; throw data.detail;
} }
if (data.status) { if (data.status) {
if (data.digest) { if (data.digest) {
let downloadProgress = 0; let downloadProgress = 0;
if (data.completed) { if (data.completed) {
downloadProgress = Math.round((data.completed / data.total) * 1000) / 10; downloadProgress = Math.round((data.completed / data.total) * 1000) / 10;
} else { } else {
downloadProgress = 100; downloadProgress = 100;
} }
modelDownloadStatus[opts.modelName] = {pullProgress: downloadProgress, digest: data.digest}; modelDownloadStatus[opts.modelName] = {
pullProgress: downloadProgress,
digest: data.digest
};
}
} }
}
} }
} }
} catch (error) { } catch (error) {
console.log('Failed to read from data stream', error); console.log(error);
throw error; opts.callback({ success: false, error, modelName: opts.modelName });
} }
} }
opts.callback({success: true, modelName: opts.modelName}); opts.callback({ success: true, modelName: opts.modelName });
} catch (error) {
console.error(error);
opts.callback({success:false, error, 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; 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'); toast.error('Maximum of 3 models can be downloading simultaneously. Please try again later');
return; return;
} }
modelTransferring = true; modelTransferring = true;
modelDownloadQueue.push({modelName: modelTag},async (data:{modelName: string; success: boolean; error?: Error}) => { modelDownloadQueue.push(
const {modelName} = data; { modelName: modelTag },
// Remove the downloaded model async (data: { modelName: string; success: boolean; error?: Error }) => {
delete modelDownloadStatus[modelName]; const { modelName } = data;
// Remove the downloaded model
delete modelDownloadStatus[modelName];
if(!data.success){ console.log(data);
toast.error(`There was some issue in downloading the model ${modelName}`);
return; 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 = ''; modelTag = '';
modelTransferring = false; 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;
}
}; };
const uploadModelHandler = async () => { const uploadModelHandler = async () => {
@ -1190,35 +1164,23 @@
</div> </div>
</div> </div>
{#if Object.keys(modelDownloadStatus).length > 0} {#if Object.keys(modelDownloadStatus).length > 0}
<table class="w-full text-sm text-left text-gray-500 dark:text-gray-400"> {#each Object.entries(modelDownloadStatus) as [modelName, payload]}
<thead <div class="flex flex-col">
class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400" <div class="font-medium mb-0.5">{modelName}</div>
> <div class="">
<tr>
<th scope="col" class="px-6 py-3"> Model Name </th>
<th scope="col" class="px-6 py-3"> Download progress </th>
</tr>
</thead>
<tbody>
{#each Object.entries(modelDownloadStatus) as [modelName, payload]}
<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
<td class="px-6 py-4">{modelName}</td>
<td class="px-6 py-4">
<div <div
class="dark:bg-gray-600 bg-green-600 text-xs font-medium text-blue-100 text-center p-0.5 leading-none rounded-full" 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)}%" style="width: {Math.max(15, payload.pullProgress ?? 0)}%"
> >
{ payload.pullProgress ?? 0}% {payload.pullProgress ?? 0}%
</div>
<div class="mt-1 text-xs dark:text-gray-500" style="font-size: 0.5rem;">
{payload.digest}
</div>
</div> </div>
<div class="mt-1 text-xs dark:text-gray-500" style="font-size: 0.5rem;"> </div>
{payload.digest} {/each}
</div> {/if}
</td>
</tr>
{/each}
</tbody>
</table>
{/if}
<hr class=" dark:border-gray-700" /> <hr class=" dark:border-gray-700" />
<div> <div>

View file

@ -127,3 +127,38 @@ export const findWordIndices = (text) => {
return matches; 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;
}
};