diff --git a/apps/shinkai-desktop/src/components/shinkai-node-manager/components/model-quality-tag.tsx b/apps/shinkai-desktop/src/components/shinkai-node-manager/components/model-quality-tag.tsx index 9377801ae..a86b4b1e7 100644 --- a/apps/shinkai-desktop/src/components/shinkai-node-manager/components/model-quality-tag.tsx +++ b/apps/shinkai-desktop/src/components/shinkai-node-manager/components/model-quality-tag.tsx @@ -9,13 +9,18 @@ export const ModelQuailityTag = ({ quality: OllamaModelQuality; }) => { const colorMap: { [key in OllamaModelQuality]: string } = { - [OllamaModelQuality.Bad]: 'border-red-700 bg-red-400 text-red-600', - [OllamaModelQuality.Medium]: - 'border-yellow-700 bg-yellow-400 text-yellow-600', - [OllamaModelQuality.Great]: 'border-green-700 bg-green-400 text-green-600', + [OllamaModelQuality.Bad]: 'bg-red-900 text-red-400', + [OllamaModelQuality.Medium]: 'text-yellow-400 bg-yellow-900', + [OllamaModelQuality.Great]: 'text-green-400 bg-green-900', }; return ( - + {quality} ); diff --git a/apps/shinkai-desktop/src/components/shinkai-node-manager/components/model-speed-tag.tsx b/apps/shinkai-desktop/src/components/shinkai-node-manager/components/model-speed-tag.tsx index bce83b0c4..4b45b5bcc 100644 --- a/apps/shinkai-desktop/src/components/shinkai-node-manager/components/model-speed-tag.tsx +++ b/apps/shinkai-desktop/src/components/shinkai-node-manager/components/model-speed-tag.tsx @@ -1,4 +1,3 @@ -import { Badge } from '@shinkai_network/shinkai-ui'; import { cn } from '@shinkai_network/shinkai-ui/utils'; import { OllamaModelSpeed } from '../../../lib/shinkai-node-manager/ollama-models'; @@ -12,8 +11,8 @@ export const ModelSpeedTag = ({ speed }: { speed: OllamaModelSpeed }) => { [OllamaModelSpeed.VeryFast]: '🐆', }; return ( - +
{speed} {emojiMap[speed]} - +
); }; diff --git a/apps/shinkai-desktop/src/components/shinkai-node-manager/ollama-models.tsx b/apps/shinkai-desktop/src/components/shinkai-node-manager/ollama-models.tsx index 468b34d14..1bb016321 100644 --- a/apps/shinkai-desktop/src/components/shinkai-node-manager/ollama-models.tsx +++ b/apps/shinkai-desktop/src/components/shinkai-node-manager/ollama-models.tsx @@ -1,6 +1,5 @@ import { useSyncOllamaModels } from '@shinkai_network/shinkai-node-state/lib/mutations/syncOllamaModels/useSyncOllamaModels'; import { - Badge, Button, Progress, ScrollArea, @@ -11,10 +10,12 @@ import { TableHeader, TableRow, } from '@shinkai_network/shinkai-ui'; +import { useMap } from '@shinkai_network/shinkai-ui/hooks'; import { cn } from '@shinkai_network/shinkai-ui/utils'; -import { Loader2 } from 'lucide-react'; +import { motion } from 'framer-motion'; +import { Download, Loader2, Minus } from 'lucide-react'; import { ModelResponse, ProgressResponse } from 'ollama/browser'; -import { useEffect, useRef, useState } from 'react'; +import { useEffect } from 'react'; import { toast } from 'sonner'; import { @@ -36,9 +37,15 @@ export const OllamaModels = () => { const auth = useAuth((auth) => auth.auth); const { data: ollamaApiUrl } = useShinkaiNodeGetOllamaApiUrlQuery(); const ollamaConfig = { host: ollamaApiUrl || 'http://127.0.0.1:11435' }; + + const installedOllamaModelsMap = useMap(); + const pullingModelsMap = useMap(); + const { data: isShinkaiNodeRunning } = useShinkaiNodeIsRunningQuery(); const { mutateAsync: shinkaiNodeSpawn } = useShinkaiNodeSpawnMutation({}); - const { mutateAsync: syncOllamaModels } = useSyncOllamaModels(OLLAMA_MODELS.map((value) => value.fullName)); + const { mutateAsync: syncOllamaModels } = useSyncOllamaModels( + OLLAMA_MODELS.map((value) => value.fullName), + ); const { isLoading: isOllamaListLoading, data: installedOllamaModels } = useOllamaListQuery(ollamaConfig, {}); const { mutateAsync: ollamaPull } = useOllamaPullMutation(ollamaConfig, { @@ -46,15 +53,13 @@ export const OllamaModels = () => { handlePullProgress(input.model, data); }, onError: (_, input) => { - pullingModelsMap.current = { - ...pullingModelsMap.current, - [input.model]: undefined, - }; + pullingModelsMap.delete(input.model); }, }); const { mutateAsync: ollamaRemove } = useOllamaRemoveMutation(ollamaConfig, { onSuccess: (_, input) => { toast.success(`Model ${input.model} removed`); + installedOllamaModelsMap.delete(input.model); }, onError: (error, input) => { toast.error(`Error removing ${input.model}. ${error.message}`); @@ -70,10 +75,7 @@ export const OllamaModels = () => { if (!progress) { continue; } - pullingModelsMap.current = { - ...pullingModelsMap.current, - [model]: progress, - }; + pullingModelsMap.set(model, progress); if (progress.status === 'success') { toast.success(`Model ${model} pull completed`); if (auth) { @@ -97,36 +99,20 @@ export const OllamaModels = () => { } catch (error) { toast.error(`Error pulling model ${model}. ${error?.toString()}`); } finally { - pullingModelsMap.current = { - ...pullingModelsMap.current, - [model]: undefined, - }; + pullingModelsMap.delete(model); } }; - const [installedOllamaModelsMap, setInstalledOllamaModelsMap] = useState( - new Map(), - ); - // const [pullingModelsMap, setPullingModelsMap] = useState<{ - // [model: string]: ProgressResponse | undefined; - // }>(); - const pullingModelsMap = useRef<{ - [model: string]: ProgressResponse | undefined; - }>(); const getProgress = (progress: ProgressResponse): number => { - return Math.ceil((100 * progress.completed) / progress.total); + return Math.ceil((100 * (progress.completed ?? 0)) / (progress.total ?? 1)); }; useEffect(() => { - setInstalledOllamaModelsMap( - new Map( - installedOllamaModels?.models.map((modelResponse) => [ - modelResponse.name, - modelResponse, - ]) || [], - ), - ); - }, [installedOllamaModels, setInstalledOllamaModelsMap]); + installedOllamaModels?.models && + installedOllamaModels.models.forEach((modelResponse) => { + installedOllamaModelsMap.set(modelResponse.name, modelResponse); + }); + }, [installedOllamaModels]); if (!isShinkaiNodeRunning) { return ( @@ -149,33 +135,34 @@ export const OllamaModels = () => { ); } return ( - - - + +
+ - AI Name + AI Name Data Limit Quality Speed - Size - + Size + {OLLAMA_MODELS.map((model) => { return ( - + -
-
- {model.name} -
- +
+ {model.name} + {/**/} + {/* {model.fullName}*/} + {/**/} + {model.description} - - {model.fullName} -
@@ -189,40 +176,57 @@ export const OllamaModels = () => { {model.size} GB - {isOllamaListLoading ? ( - - ) : installedOllamaModelsMap.has(model.fullName) ? ( - - ) : pullingModelsMap.current?.[model.fullName] ? ( -
- - - {pullingModelsMap.current?.[model.fullName]?.status} - -
- ) : ( - - )} + + {isOllamaListLoading ? ( + + ) : installedOllamaModelsMap.has(model.fullName) ? ( + + ) : pullingModelsMap.get(model.fullName) ? ( +
+ + {getProgress( + pullingModelsMap.get( + model.fullName, + ) as ProgressResponse, + ) + '%'} + + + + {pullingModelsMap.get(model.fullName)?.status} + +
+ ) : ( + + )} +
); diff --git a/apps/shinkai-desktop/src/pages/ai-model-installation.tsx b/apps/shinkai-desktop/src/pages/ai-model-installation.tsx index 35bd7ef5b..1225bbfaf 100644 --- a/apps/shinkai-desktop/src/pages/ai-model-installation.tsx +++ b/apps/shinkai-desktop/src/pages/ai-model-installation.tsx @@ -1,32 +1,37 @@ import { buttonVariants } from '@shinkai_network/shinkai-ui'; import { cn } from '@shinkai_network/shinkai-ui/utils'; +import { QueryClientProvider } from '@tanstack/react-query'; +import { ArrowRight } from 'lucide-react'; import { Link } from 'react-router-dom'; -import { SubpageLayout } from './layout/simple-layout'; +import { OllamaModels } from '../components/shinkai-node-manager/ollama-models'; +import { queryClient } from '../lib/shinkai-node-manager/shinkai-node-manager-client'; +import { FixedHeaderLayout } from './layout/simple-layout'; const AIModelInstallation = () => { return ( - -
- AI Model Installation -
- + - Continue - -
+ +
+ + Continue + + +
+ + ); }; diff --git a/apps/shinkai-desktop/src/pages/get-started.tsx b/apps/shinkai-desktop/src/pages/get-started.tsx index c902509a5..91c6251cf 100644 --- a/apps/shinkai-desktop/src/pages/get-started.tsx +++ b/apps/shinkai-desktop/src/pages/get-started.tsx @@ -42,7 +42,9 @@ const GetStartedPage = () => { node_encryption_pk: response.data?.encryption_public_key ?? '', }; setAuth(updatedSetupData); - navigate('/'); + // Hide http subscription for now + // navigate('/connect-ai'); + navigate('/ai-model-installation'); } else { throw new Error('Failed to submit registration'); } @@ -73,27 +75,12 @@ const GetStartedPage = () => { } return ( -
+

Transform your desktop experience using AI with Shinkai Desktop{' '} 🔑

- {/* Note: Temporary disabled, model manager and http subscriptions are work in progress */} - {/**/} - {/* Shinkai Private (Local)*/} - {/**/}
+ )); TableHeader.displayName = 'TableHeader'; @@ -26,11 +30,7 @@ const TableBody = React.forwardRef< HTMLTableSectionElement, React.HTMLAttributes >(({ className, ...props }, ref) => ( - + )); TableBody.displayName = 'TableBody'; @@ -55,7 +55,7 @@ const TableRow = React.forwardRef< >(({ className, ...props }, ref) => ( (({ className, ...props }, ref) => (
(initialState: [Key, Value][] = []) { + const mapRef = React.useRef(new Map(initialState)); + const [, reRender] = React.useReducer((x) => x + 1, 0); + + mapRef.current.set = (...args) => { + Map.prototype.set.apply(mapRef.current, args); + reRender(); + return mapRef.current; + }; + + mapRef.current.clear = (...args) => { + Map.prototype.clear.apply(mapRef.current, args); + reRender(); + }; + + mapRef.current.delete = (...args) => { + const res = Map.prototype.delete.apply(mapRef.current, args); + reRender(); + + return res; + }; + + return mapRef.current; +}