11<script setup lang="ts">
2+ import type { Model } from " ~/utils/db/local" ;
3+
24interface Models {
3- openai: string [];
4- gemini: string [];
5- anthropic: string [];
6- ollama: Record <string , string []>;
5+ openai: Model [];
6+ gemini: Model [];
7+ anthropic: Model [];
8+ ollama: Record <string , Model []>;
79}
810const models = ref <Models >({
911 openai: [],
@@ -13,7 +15,7 @@ const models = ref<Models>({
1315});
1416async function fetchModels(provider : keyof Models , url ? : string ) {
1517 if (provider === " ollama" && url ) {
16- const response = await $fetch <{ models: string [] }>(
18+ const response = await $fetch <{ models: Model [] }>(
1719 ` /api/models/ollama?url=${encodeURIComponent (url )} ` ,
1820 );
1921 models .value .ollama [url ] = response .models ;
@@ -22,66 +24,58 @@ async function fetchModels(provider: keyof Models, url?: string) {
2224 provider === " gemini" ||
2325 provider === " anthropic"
2426 ) {
25- const response = await $fetch <{ models: string [] }>(
27+ const response = await $fetch <{ models: Model [] }>(
2628 ` /api/models/${provider } ` ,
2729 );
28- console .log (provider , response );
2930 models .value [provider ] = response .models ;
3031 }
3132}
3233const globalSettingsStore = useGlobalSettingsStore ();
3334const availableModels = computed (() => {
3435 return globalSettingsStore .settings .availableModels ;
3536});
36- function checkAvailableModel(model : string , url ? : string ) {
37- if (url ) {
37+ function checkAvailableModel(model : Model ) {
38+ if (model . url ) {
3839 return availableModels .value .some (
39- (m ) => m .name === model && m .provider === " ollama" && m .url === url ,
40+ (m ) =>
41+ m .name === model .name && m .provider === " ollama" && m .url === model .url ,
4042 );
4143 }
42- return availableModels .value .some ((m ) => m .name === model );
44+ return availableModels .value .some ((m ) => m .name === model . name );
4345}
4446
45- function addModelToAvailableModels(
46- model : string ,
47- provider : string ,
48- url ? : string ,
49- ) {
47+ function addModelToAvailableModels(model : Model ) {
5048 globalSettingsStore .updateSettings ({
51- availableModels: [... availableModels .value , { name: model , provider , url }],
49+ availableModels: [... availableModels .value , { ... model }],
5250 });
5351}
54- function removeModelFromAvailableModels(
55- model : string ,
56- provider : string ,
57- url ? : string ,
58- ) {
52+ function removeModelFromAvailableModels(model : Model ) {
5953 globalSettingsStore .updateSettings ({
6054 availableModels: availableModels .value .filter (
6155 (m ) =>
62- (m .name !== model && m .url === url ) ||
63- m .name !== model ||
64- m .provider !== provider ||
65- m .url !== url ,
56+ (m .name !== model . name && m .url === model . url ) ||
57+ m .name !== model . name ||
58+ m .provider !== model . provider ||
59+ m .url !== model . url ,
6660 ),
6761 });
6862}
69- function updateAvailableModels(model : string , provider : string , url ? : string ) {
70- if (checkAvailableModel (model , url )) {
71- removeModelFromAvailableModels (model , provider , url );
63+ function updateAvailableModels(model : Model ) {
64+ if (checkAvailableModel (model )) {
65+ removeModelFromAvailableModels (model );
7266 } else {
73- addModelToAvailableModels (model , provider , url );
67+ addModelToAvailableModels (model );
7468 }
7569}
7670
77- function checkModelAgainstEndpoint(model : string , url ? : string ) {
78- if (url ) {
79- return models .value .ollama [url ]?.some ((m ) => m === model );
71+ function checkModelAgainstEndpoint(model : Model ) {
72+ if (model . url ) {
73+ return models .value .ollama [model . url ]?.some ((m ) => m . name === model . name );
8074 }
8175 return (
82- models .value .openai .some ((m ) => m === model ) ||
83- models .value .gemini .some ((m ) => m === model ) ||
84- models .value .anthropic .some ((m ) => m === model )
76+ models .value .openai .some ((m ) => m . name === model . name ) ||
77+ models .value .gemini .some ((m ) => m . name === model . name ) ||
78+ models .value .anthropic .some ((m ) => m . name === model . name )
8579 );
8680}
8781
@@ -128,7 +122,7 @@ async function testOllamaUrl(url: string) {
128122}
129123
130124const deleteOllamaModelModalVisible = ref (false );
131- const ollamaModelToDelete = ref <{ name : string ; url : string } | null >(null );
125+ const ollamaModelToDelete = ref <Model | null >(null );
132126async function deleteOllamaModel() {
133127 const model = ollamaModelToDelete .value ;
134128 if (! model ?.url || ! model .name ) {
@@ -143,6 +137,8 @@ async function deleteOllamaModel() {
143137 );
144138
145139 if (response .success ) {
140+ // Remove the model from the available models
141+ removeModelFromAvailableModels (model );
146142 await fetchModels (" ollama" , model .url );
147143 deleteOllamaModelModalVisible .value = false ;
148144 }
@@ -202,7 +198,7 @@ async function pullOllamaModel(url: string) {
202198 }
203199 modelToPull .value = {};
204200 isPulling .value [url ] = false ;
205- fetchModels (" ollama" );
201+ fetchModels (" ollama" , url );
206202}
207203
208204const openaiAvailable = ref (false );
@@ -245,17 +241,19 @@ onMounted(async () => {
245241 class =" w-full grid grid-cols-2 md:grid-cols-3 gap-2 text-nowrap mb-3"
246242 >
247243 <div
248- v-for =" model in models?.openai.sort((a, b) => a.localeCompare(b))"
249- :key =" model"
244+ v-for =" model in models?.openai.sort((a, b) =>
245+ a.name.localeCompare(b.name),
246+ )"
247+ :key =" model.name"
250248 :class =" [
251249 'flex border border-(--main-color) rounded-full px-3 cursor-pointer ',
252250 checkAvailableModel(model)
253251 ? 'bg-(--main-color) text-(--bg-color)'
254252 : 'text-(--text-color)',
255253 ]"
256- @click =" updateAvailableModels(model, 'openai' )"
254+ @click =" updateAvailableModels(model)"
257255 >
258- <HoverScrollText >{{ model }}</HoverScrollText >
256+ <HoverScrollText >{{ model.displayName }}</HoverScrollText >
259257 </div >
260258 </div >
261259 </SettingsGroup >
@@ -270,17 +268,19 @@ onMounted(async () => {
270268 class =" w-full grid grid-cols-2 md:grid-cols-3 gap-2 text-nowrap mb-3"
271269 >
272270 <div
273- v-for =" model in models?.gemini.sort((a, b) => a.localeCompare(b))"
274- :key =" model"
271+ v-for =" model in models?.gemini.sort((a, b) =>
272+ a.name.localeCompare(b.name),
273+ )"
274+ :key =" model.name"
275275 :class =" [
276276 'flex border border-(--main-color) rounded-full px-3 cursor-pointer ',
277277 checkAvailableModel(model)
278278 ? 'bg-(--main-color) text-(--bg-color)'
279279 : 'text-(--text-color)',
280280 ]"
281- @click =" updateAvailableModels(model, 'gemini' )"
281+ @click =" updateAvailableModels(model)"
282282 >
283- <HoverScrollText >{{ model }}</HoverScrollText >
283+ <HoverScrollText >{{ model.displayName }}</HoverScrollText >
284284 </div >
285285 </div >
286286 </SettingsGroup >
@@ -295,17 +295,19 @@ onMounted(async () => {
295295 class =" w-full grid grid-cols-2 md:grid-cols-3 gap-2 text-nowrap mb-3"
296296 >
297297 <div
298- v-for =" model in models?.anthropic.sort((a, b) => a.localeCompare(b))"
299- :key =" model"
298+ v-for =" model in models?.anthropic.sort((a, b) =>
299+ a.name.localeCompare(b.name),
300+ )"
301+ :key =" model.name"
300302 :class =" [
301303 'flex border border-(--main-color) rounded-full px-3 cursor-pointer ',
302304 checkAvailableModel(model)
303305 ? 'bg-(--main-color) text-(--bg-color)'
304306 : 'text-(--text-color)',
305307 ]"
306- @click =" updateAvailableModels(model, 'anthropic' )"
308+ @click =" updateAvailableModels(model)"
307309 >
308- <HoverScrollText >{{ model }}</HoverScrollText >
310+ <HoverScrollText >{{ model.displayName }}</HoverScrollText >
309311 </div >
310312 </div >
311313 </SettingsGroup >
@@ -394,24 +396,24 @@ onMounted(async () => {
394396 >
395397 <div
396398 v-for =" model in (models?.ollama[url] || []).sort((a, b) =>
397- a.localeCompare(b),
399+ a.name. localeCompare(b.name ),
398400 )"
399- :key =" model"
401+ :key =" model.name "
400402 :class =" [
401403 'flex items-center justify-between gap-2 border border-(--main-color) rounded-full text-center truncate px-3 cursor-pointer',
402- checkAvailableModel(model, url )
404+ checkAvailableModel(model)
403405 ? 'bg-(--main-color) text-(--bg-color)'
404406 : 'text-(--text-color)',
405407 ]"
406- @click =" updateAvailableModels(model, 'ollama', url )"
408+ @click =" updateAvailableModels(model)"
407409 >
408- <HoverScrollText >{{ model }}</HoverScrollText >
410+ <HoverScrollText >{{ model.displayName }}</HoverScrollText >
409411 <Icon
410412 name =" lucide:trash-2"
411413 class =" text-(--text-color) ml-1"
412414 @click.stop.prevent ="
413415 () => {
414- ollamaModelToDelete = { name: model, url } ;
416+ ollamaModelToDelete = model;
415417 deleteOllamaModelModalVisible = true;
416418 }
417419 "
@@ -421,17 +423,14 @@ onMounted(async () => {
421423 </div >
422424 </SettingsGroup >
423425 <SettingsGroup
424- v-if ="
425- availableModels.filter((m) => !checkModelAgainstEndpoint(m.name, m.url))
426- .length
427- "
426+ v-if =" availableModels.filter((m) => !checkModelAgainstEndpoint(m)).length"
428427 title =" leftover models"
429428 icon =" lucide:alert-triangle"
430429 description =" these models are currently not available via an api endpoint, but are still present in the system."
431430 >
432431 <div
433432 v-for =" model in availableModels.filter(
434- (m) => !checkModelAgainstEndpoint(m.name, m.url ),
433+ (m) => !checkModelAgainstEndpoint(m),
435434 )"
436435 :key =" model.name"
437436 class =" flex items-center gap-2 mb-4"
@@ -443,11 +442,7 @@ onMounted(async () => {
443442 class =" bg-(--bg-color) text-(--error-color) rounded px-1! py-1! cursor-pointer"
444443 @click.prevent.stop ="
445444 () => {
446- removeModelFromAvailableModels(
447- model.name,
448- model.provider,
449- model.url,
450- );
445+ removeModelFromAvailableModels(model);
451446 }
452447 "
453448 >
0 commit comments