浏览代码

feat: modelfile builder tagName field added

Timothy J. Baek 1 年之前
父节点
当前提交
12d7ae96b9

+ 1 - 0
src/lib/stores/index.ts

@@ -9,5 +9,6 @@ export const db = writable(undefined);
 export const chatId = writable('');
 export const chatId = writable('');
 export const chats = writable([]);
 export const chats = writable([]);
 export const models = writable([]);
 export const models = writable([]);
+export const modelfiles = writable([]);
 export const settings = writable({});
 export const settings = writable({});
 export const showSettings = writable(false);
 export const showSettings = writable(false);

+ 21 - 3
src/routes/(app)/+layout.svelte

@@ -4,7 +4,17 @@
 	import { onMount, tick } from 'svelte';
 	import { onMount, tick } from 'svelte';
 	import { goto } from '$app/navigation';
 	import { goto } from '$app/navigation';
 
 
-	import { config, user, showSettings, settings, models, db, chats, chatId } from '$lib/stores';
+	import {
+		config,
+		user,
+		showSettings,
+		settings,
+		models,
+		db,
+		chats,
+		chatId,
+		modelfiles
+	} from '$lib/stores';
 
 
 	import SettingsModal from '$lib/components/chat/SettingsModal.svelte';
 	import SettingsModal from '$lib/components/chat/SettingsModal.svelte';
 	import Sidebar from '$lib/components/layout/Sidebar.svelte';
 	import Sidebar from '$lib/components/layout/Sidebar.svelte';
@@ -78,7 +88,7 @@
 	};
 	};
 
 
 	const getDB = async () => {
 	const getDB = async () => {
-		const _db = await openDB('Chats', 1, {
+		const DB = await openDB('Chats', 1, {
 			upgrade(db) {
 			upgrade(db) {
 				const store = db.createObjectStore('chats', {
 				const store = db.createObjectStore('chats', {
 					keyPath: 'id',
 					keyPath: 'id',
@@ -89,7 +99,7 @@
 		});
 		});
 
 
 		return {
 		return {
-			db: _db,
+			db: DB,
 			getChatById: async function (id) {
 			getChatById: async function (id) {
 				return await this.db.get('chats', id);
 				return await this.db.get('chats', id);
 			},
 			},
@@ -162,6 +172,14 @@
 		let _db = await getDB();
 		let _db = await getDB();
 		await db.set(_db);
 		await db.set(_db);
 
 
+		await modelfiles.set(
+			JSON.parse(localStorage.getItem('modelfiles') ?? JSON.stringify($modelfiles))
+		);
+
+		modelfiles.subscribe(async () => {
+			await models.set(await getModels());
+		});
+
 		await tick();
 		await tick();
 		loaded = true;
 		loaded = true;
 	});
 	});

+ 47 - 11
src/routes/(app)/modelfiles/+page.svelte

@@ -1,12 +1,19 @@
+<script lang="ts">
+	import { modelfiles } from '$lib/stores';
+	import { onMount } from 'svelte';
+
+	onMount(() => {});
+</script>
+
 <div class="min-h-screen w-full flex justify-center dark:text-white">
 <div class="min-h-screen w-full flex justify-center dark:text-white">
 	<div class=" py-2.5 flex flex-col justify-between w-full">
 	<div class=" py-2.5 flex flex-col justify-between w-full">
 		<div class="max-w-2xl mx-auto w-full px-3 md:px-0 my-10">
 		<div class="max-w-2xl mx-auto w-full px-3 md:px-0 my-10">
 			<div class=" text-2xl font-semibold mb-6">My Modelfiles</div>
 			<div class=" text-2xl font-semibold mb-6">My Modelfiles</div>
 
 
-			<a class=" flex space-x-3 cursor-pointer w-full mb-3" href="/modelfiles/create">
-				<div class=" self-center">
+			<a class=" flex space-x-4 cursor-pointer w-full mb-3" href="/modelfiles/create">
+				<div class=" self-center w-10">
 					<div
 					<div
-						class=" p-2 rounded-full bg-transparent dark:bg-gray-700 border border-dashed border-gray-200"
+						class="w-full h-10 flex justify-center rounded-full bg-transparent dark:bg-gray-700 border border-dashed border-gray-200"
 					>
 					>
 						<svg
 						<svg
 							xmlns="http://www.w3.org/2000/svg"
 							xmlns="http://www.w3.org/2000/svg"
@@ -29,12 +36,41 @@
 				</div>
 				</div>
 			</a>
 			</a>
 
 
-			<!-- <div class=" my-16">
-				<div class=" text-2xl font-semibold mb-6">Made by Community</div>
+			{#each $modelfiles as modelfile}
+				<hr class=" dark:border-gray-700 my-2.5" />
+				<a
+					class=" flex space-x-4 cursor-pointer w-full mb-3"
+					href={`/?models=${modelfile.tagName}`}
+				>
+					<div class=" self-center w-10">
+						<div class=" rounded-full bg-stone-700">
+							<img
+								src={modelfile.imageUrl ?? '/user.png'}
+								alt="modelfile profile"
+								class=" rounded-full w-full h-auto object-cover"
+							/>
+						</div>
+					</div>
+
+					<div class=" flex-1 self-center">
+						<div class=" font-bold">{modelfile.title}</div>
+						<div class=" text-sm overflow-hidden text-ellipsis line-clamp-2">{modelfile.desc}</div>
+					</div>
+				</a>
+			{/each}
+
+			<div class=" my-16">
+				<div class=" text-2xl font-semibold mb-6">Made by OllamaHub Community</div>
 
 
-				<a class=" flex space-x-3 cursor-pointer w-full mb-3" href="/presets/create">
-					<div class=" self-center">
-						<div class=" p-2 rounded-full bg-gray-700 border border-dashed border-gray-200">
+				<a
+					class=" flex space-x-4 cursor-pointer w-full mb-3"
+					href="https://ollamahub.com/"
+					target="_blank"
+				>
+					<div class=" self-center w-10">
+						<div
+							class="w-full h-10 flex justify-center rounded-full bg-transparent dark:bg-gray-700 border border-dashed border-gray-200"
+						>
 							<svg
 							<svg
 								xmlns="http://www.w3.org/2000/svg"
 								xmlns="http://www.w3.org/2000/svg"
 								viewBox="0 0 24 24"
 								viewBox="0 0 24 24"
@@ -51,11 +87,11 @@
 					</div>
 					</div>
 
 
 					<div class=" self-center">
 					<div class=" self-center">
-						<div class=" font-bold">Create a preset</div>
-						<div class=" text-sm">Customize Ollama models for a specific purpose</div>
+						<div class=" font-bold">Discover a modelfile</div>
+						<div class=" text-sm">Discover, download, and explore Ollama Modelfiles</div>
 					</div>
 					</div>
 				</a>
 				</a>
-			</div> -->
+			</div>
 		</div>
 		</div>
 	</div>
 	</div>
 </div>
 </div>

+ 72 - 17
src/routes/(app)/modelfiles/create/+page.svelte

@@ -1,8 +1,9 @@
 <script>
 <script>
+	import { v4 as uuidv4 } from 'uuid';
 	import { toast } from 'svelte-french-toast';
 	import { toast } from 'svelte-french-toast';
 	import { goto } from '$app/navigation';
 	import { goto } from '$app/navigation';
 	import { OLLAMA_API_BASE_URL } from '$lib/constants';
 	import { OLLAMA_API_BASE_URL } from '$lib/constants';
-	import { settings, db, user, config } from '$lib/stores';
+	import { settings, db, user, config, modelfiles } from '$lib/stores';
 
 
 	import Advanced from '$lib/components/chat/Settings/Advanced.svelte';
 	import Advanced from '$lib/components/chat/Settings/Advanced.svelte';
 	import { splitStream } from '$lib/utils';
 	import { splitStream } from '$lib/utils';
@@ -21,6 +22,7 @@
 	// ///////////
 	// ///////////
 
 
 	let title = '';
 	let title = '';
+	let tagName = '';
 	let desc = '';
 	let desc = '';
 
 
 	let raw = true;
 	let raw = true;
@@ -49,6 +51,8 @@
 		num_ctx: ''
 		num_ctx: ''
 	};
 	};
 
 
+	$: tagName = title !== '' ? `${title.replace(/\s+/g, '-').toLowerCase()}:latest` : '';
+
 	$: if (!raw) {
 	$: if (!raw) {
 		content = `FROM ${model}
 		content = `FROM ${model}
 ${template !== '' ? `TEMPLATE """${template}"""` : ''}
 ${template !== '' ? `TEMPLATE """${template}"""` : ''}
@@ -85,6 +89,11 @@ SYSTEM """${system}"""`.replace(/^\s*\n/gm, '');
 		Business: false
 		Business: false
 	};
 	};
 
 
+	const saveModelfile = async (modelfile) => {
+		await modelfiles.set([...$modelfiles, modelfile]);
+		localStorage.setItem('modelfiles', JSON.stringify($modelfiles));
+	};
+
 	const submitHandler = async () => {
 	const submitHandler = async () => {
 		loading = true;
 		loading = true;
 
 
@@ -108,7 +117,7 @@ SYSTEM """${system}"""`.replace(/^\s*\n/gm, '');
 					...($user && { Authorization: `Bearer ${localStorage.token}` })
 					...($user && { Authorization: `Bearer ${localStorage.token}` })
 				},
 				},
 				body: JSON.stringify({
 				body: JSON.stringify({
-					name: title.replace(/\s+/g, '-').toLowerCase(),
+					name: tagName,
 					modelfile: content
 					modelfile: content
 				})
 				})
 			});
 			});
@@ -170,8 +179,22 @@ SYSTEM """${system}"""`.replace(/^\s*\n/gm, '');
 					}
 					}
 				}
 				}
 			}
 			}
+
+			if (success) {
+				await saveModelfile({
+					tagName: tagName,
+					imageUrl: imageUrl,
+					title: title,
+					desc: desc,
+					content: content,
+					suggestionPrompts: suggestions.filter((prompt) => prompt.content !== ''),
+					categories: Object.keys(categories).filter((category) => categories[category])
+				});
+				await goto('/modelfiles');
+			}
 		}
 		}
 		loading = false;
 		loading = false;
+		success = false;
 	};
 	};
 </script>
 </script>
 
 
@@ -196,15 +219,32 @@ SYSTEM """${system}"""`.replace(/^\s*\n/gm, '');
 							const canvas = document.createElement('canvas');
 							const canvas = document.createElement('canvas');
 							const ctx = canvas.getContext('2d');
 							const ctx = canvas.getContext('2d');
 
 
-							// Set canvas dimensions to the original image dimensions
-							canvas.width = img.width;
-							canvas.height = img.height;
+							// Calculate the aspect ratio of the image
+							const aspectRatio = img.width / img.height;
+
+							// Calculate the new width and height to fit within 100x100
+							let newWidth, newHeight;
+							if (aspectRatio > 1) {
+								newWidth = 100 * aspectRatio;
+								newHeight = 100;
+							} else {
+								newWidth = 100;
+								newHeight = 100 / aspectRatio;
+							}
+
+							// Set the canvas size
+							canvas.width = 100;
+							canvas.height = 100;
 
 
-							// Draw the original image on the canvas
-							ctx.drawImage(img, 0, 0);
+							// Calculate the position to center the image
+							const offsetX = (100 - newWidth) / 2;
+							const offsetY = (100 - newHeight) / 2;
+
+							// Draw the image on the canvas
+							ctx.drawImage(img, offsetX, offsetY, newWidth, newHeight);
 
 
 							// Get the base64 representation of the compressed image
 							// Get the base64 representation of the compressed image
-							const compressedSrc = canvas.toDataURL('image/jpeg', 0.1);
+							const compressedSrc = canvas.toDataURL('image/jpeg');
 
 
 							// Display the compressed image
 							// Display the compressed image
 							imageUrl = compressedSrc;
 							imageUrl = compressedSrc;
@@ -293,16 +333,31 @@ SYSTEM """${system}"""`.replace(/^\s*\n/gm, '');
 					</div>
 					</div>
 				</div>
 				</div>
 
 
-				<div class="my-2">
-					<div class=" text-sm font-semibold mb-2">Name*</div>
+				<div class="my-2 flex space-x-2">
+					<div class="flex-1">
+						<div class=" text-sm font-semibold mb-2">Name*</div>
+
+						<div>
+							<input
+								class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg"
+								placeholder="Name your modelfile"
+								bind:value={title}
+								required
+							/>
+						</div>
+					</div>
 
 
-					<div>
-						<input
-							class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg"
-							placeholder="Name your modelfile"
-							bind:value={title}
-							required
-						/>
+					<div class="flex-1">
+						<div class=" text-sm font-semibold mb-2">Model Tag Name*</div>
+
+						<div>
+							<input
+								class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg"
+								placeholder="Add a model tag name"
+								bind:value={tagName}
+								required
+							/>
+						</div>
 					</div>
 					</div>
 				</div>
 				</div>