forked from open-webui/open-webui
feat: modelfile builder tagName field added
This commit is contained in:
parent
382827c71d
commit
12d7ae96b9
4 changed files with 141 additions and 31 deletions
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
});
|
});
|
||||||
|
|
|
@ -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">
|
<a class=" flex space-x-4 cursor-pointer w-full mb-3" href="/modelfiles/create">
|
||||||
<div class=" self-center">
|
<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">
|
{#each $modelfiles as modelfile}
|
||||||
<div class=" text-2xl font-semibold mb-6">Made by Community</div>
|
<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>
|
||||||
|
|
||||||
<a class=" flex space-x-3 cursor-pointer w-full mb-3" href="/presets/create">
|
<div class=" flex-1 self-center">
|
||||||
<div class=" self-center">
|
<div class=" font-bold">{modelfile.title}</div>
|
||||||
<div class=" p-2 rounded-full bg-gray-700 border border-dashed border-gray-200">
|
<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-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=" font-bold">Discover a modelfile</div>
|
||||||
<div class=" text-sm">Customize Ollama models for a specific purpose</div>
|
<div class=" text-sm">Discover, download, and explore Ollama Modelfiles</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</div> -->
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -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
|
// Calculate the aspect ratio of the image
|
||||||
canvas.width = img.width;
|
const aspectRatio = img.width / img.height;
|
||||||
canvas.height = img.height;
|
|
||||||
|
|
||||||
// Draw the original image on the canvas
|
// Calculate the new width and height to fit within 100x100
|
||||||
ctx.drawImage(img, 0, 0);
|
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;
|
||||||
|
|
||||||
|
// 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,7 +333,8 @@ SYSTEM """${system}"""`.replace(/^\s*\n/gm, '');
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="my-2">
|
<div class="my-2 flex space-x-2">
|
||||||
|
<div class="flex-1">
|
||||||
<div class=" text-sm font-semibold mb-2">Name*</div>
|
<div class=" text-sm font-semibold mb-2">Name*</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
@ -306,6 +347,20 @@ SYSTEM """${system}"""`.replace(/^\s*\n/gm, '');
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<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 class="my-2">
|
<div class="my-2">
|
||||||
<div class=" text-sm font-semibold mb-2">Description*</div>
|
<div class=" text-sm font-semibold mb-2">Description*</div>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue