Skip to content

Commit

Permalink
feat: improve local models UI + bump shinkai node (#414)
Browse files Browse the repository at this point in the history
* fix: remove warning banner on initial screen

* improvements: models

* add provider logos

* fixes

* bump shinkai node and apps version
  • Loading branch information
paulclindo authored Aug 23, 2024
1 parent e024f2f commit aa73c5f
Show file tree
Hide file tree
Showing 16 changed files with 447 additions and 107 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pr-ci-healchecks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
- name: Download side binaries
env:
ARCH: x86_64-unknown-linux-gnu
SHINKAI_NODE_VERSION: v0.7.29
SHINKAI_NODE_VERSION: v0.7.30
OLLAMA_VERSION: v0.3.6
run: |
npx ts-node ./ci-scripts/download-side-binaries.ts
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ jobs:
- name: Download side binaries
env:
ARCH: ${{ matrix.arch }}
SHINKAI_NODE_VERSION: v0.7.29
SHINKAI_NODE_VERSION: v0.7.30
OLLAMA_VERSION: v0.3.6
run: |
npx ts-node ./ci-scripts/download-side-binaries.ts
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ jobs:
- name: Download side binaries
env:
ARCH: ${{ matrix.arch }}
SHINKAI_NODE_VERSION: v0.7.29
SHINKAI_NODE_VERSION: v0.7.30
OLLAMA_VERSION: v0.3.6
run: |
npx ts-node ./ci-scripts/download-side-binaries.ts
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,22 @@ $ git clone https://github.com/dcSpark/shinkai-apps
```
ARCH="aarch64-apple-darwin" \
OLLAMA_VERSION="v0.3.6" \
SHINKAI_NODE_VERSION="v0.7.29" \
SHINKAI_NODE_VERSION="v0.7.30" \
npx ts-node ./ci-scripts/download-side-binaries.ts
```

#### Linux
```
ARCH="x86_64-unknown-linux-gnu" \
OLLAMA_VERSION="v0.3.6"\
SHINKAI_NODE_VERSION="v0.7.29" \
SHINKAI_NODE_VERSION="v0.7.30" \
npx ts-node ./ci-scripts/download-side-binaries.ts
```

#### Windows
```
$ENV:OLLAMA_VERSION="v0.3.6"
$ENV:SHINKAI_NODE_VERSION="v0.7.29"
$ENV:SHINKAI_NODE_VERSION="v0.7.30"
$ENV:ARCH="x86_64-pc-windows-msvc"
npx ts-node ./ci-scripts/download-side-binaries.ts
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,18 @@ export const ModelCapabilityTag = ({
[key in OllamaModelCapability]: { text: string; icon: ReactNode };
} = {
[OllamaModelCapability.ImageToText]: {
icon: <Images className="h-4 w-4" />,
icon: <Images className="h-3.5 w-3.5" />,
text: t('shinkaiNode.models.labels.visionCapability'),
},
[OllamaModelCapability.TextGeneration]: {
icon: <ALargeSmall className="h-4 w-4" />,
icon: <ALargeSmall className="h-3.5 w-3.5" />,
text: t('shinkaiNode.models.labels.textCapability'),
},
};
return (
<Badge
className={cn(
'justify-center rounded-full bg-blue-700 px-2 py-1 font-normal capitalize text-blue-200',
'justify-center rounded-full border-blue-700 bg-blue-700/70 px-2 py-1 font-normal capitalize text-gray-50',
className,
)}
variant="outline"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { CheckIcon } from '@radix-ui/react-icons';
import { useTranslation } from '@shinkai_network/shinkai-i18n';
import { useSyncOllamaModels } from '@shinkai_network/shinkai-node-state/lib/mutations/syncOllamaModels/useSyncOllamaModels';
import { Button, Progress } from '@shinkai_network/shinkai-ui';
import { useMap } from '@shinkai_network/shinkai-ui/hooks';
import { cn } from '@shinkai_network/shinkai-ui/utils';
import { motion } from 'framer-motion';
import { Download, Loader2, Minus } from 'lucide-react';
import { ModelResponse, ProgressResponse } from 'ollama/browser';
import { useEffect } from 'react';
import { useEffect, useState } from 'react';
import { toast } from 'sonner';

import {
Expand Down Expand Up @@ -116,18 +119,21 @@ export const OllamaModelInstallButton = ({ model }: { model: string }) => {
{isOllamaListLoading ? (
<Loader2 className="animate-spin" />
) : installedOllamaModelsMap.has(model) ? (
<Button
className="hover:border-brand w-full py-1.5 text-sm hover:text-white"
<RemoveAIModelButton
onClick={() => {
ollamaRemove({ model: model });
}}
size="auto"
variant={'destructive'}
>
<Minus className="mr-2 h-3 w-3" />
{t('common.remove')}
</Button>
) : pullingModelsMap?.get(model) ? (
/>
) : // <Button
// className="hover:border-brand w-full py-1.5 text-sm hover:text-white"
//
// size="auto"
// variant={'destructive'}
// >
// <Minus className="mr-2 h-3 w-3" />
// {t('common.remove')}
// </Button>
pullingModelsMap?.get(model) ? (
<div className="flex flex-col items-center gap-1">
<span className="text-xs text-gray-100">
{getProgress(pullingModelsMap.get(model) as ProgressResponse) + '%'}
Expand All @@ -154,3 +160,30 @@ export const OllamaModelInstallButton = ({ model }: { model: string }) => {
</div>
);
};

const MotionButton = motion(Button);

function RemoveAIModelButton({ onClick }: { onClick: () => void }) {
const { t } = useTranslation();
const [isHovered, setIsHovered] = useState(false);
return (
<MotionButton
className={cn(
'w-full py-1.5 text-sm hover:border-red-800 hover:bg-red-700/50 hover:text-red-50',
)}
layout
onClick={onClick}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
size="auto"
variant="outline"
>
{isHovered ? (
<Minus className="mr-2 h-3 w-3" />
) : (
<CheckIcon className="text-brand mr-2 h-3.5 w-3.5" />
)}
{isHovered ? t('common.remove') : t('common.installed')}
</MotionButton>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,28 @@ import {
Badge,
Button,
ScrollArea,
Separator,
Tooltip,
TooltipContent,
TooltipPortal,
TooltipProvider,
TooltipTrigger,
} from '@shinkai_network/shinkai-ui';
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from '@shinkai_network/shinkai-ui';
import {
GoogleIcon,
MetaIcon,
MicrosoftIcon,
MistralIcon,
} from '@shinkai_network/shinkai-ui/assets';
import { cn } from '@shinkai_network/shinkai-ui/utils';
import { BookOpenText, Database, List, Star } from 'lucide-react';
import { useState } from 'react';
import { BookOpenText, Database, Star, StarIcon } from 'lucide-react';
import { useMemo, useState } from 'react';
import AutoSizer from 'react-virtualized-auto-sizer';

import { OLLAMA_MODELS } from '../../lib/shinkai-node-manager/ollama-models';
Expand Down Expand Up @@ -43,6 +52,15 @@ export const OllamaModels = () => {
return defaultModel === model;
};

const providerLogoMap = useMemo(() => {
return {
Microsoft: <MicrosoftIcon className="h-6 w-6" />,
Google: <GoogleIcon className="h-6 w-6" />,
Meta: <MetaIcon className="h-6 w-6" />,
Mistral: <MistralIcon className="h-6 w-6" />,
};
}, []);

if (!isShinkaiNodeRunning) {
return (
<div className="flex h-full w-full flex-row items-center justify-center">
Expand All @@ -65,79 +83,84 @@ export const OllamaModels = () => {
}

return (
<div className="flex h-full flex-col items-center justify-center space-y-2 py-2">
<div
className={cn(
'flex flex-col items-center justify-center gap-2 py-2',
showAllOllamaModels && 'h-full',
)}
>
{!showAllOllamaModels && (
<ScrollArea className="mt-2 flex h-full flex-1 flex-col overflow-auto [&>div>div]:!block">
<div className="flex w-full flex-row flex-wrap items-center justify-center gap-2">
<ScrollArea className="mt-1 flex flex-1 flex-col overflow-auto [&>div>div]:!block">
<div className="grid grid-cols-2 gap-4">
{OLLAMA_MODELS.map((model) => {
return (
<Card
className="grid h-[500px] w-[260px] grid-flow-row"
key={model.fullName}
>
<CardHeader className="">
<CardTitle className="text-md">
<Card className="gap- flex flex-col" key={model.fullName}>
<CardHeader className="relative">
<CardTitle className="text-md mb-3 flex items-center gap-2">
<span className="bg-gray-350 rounded-lg p-2">
{model.provider
? providerLogoMap[
model?.provider as keyof typeof providerLogoMap
]
: null}
</span>

<span>{model.name}</span>
</CardTitle>
<div className="mt-2 h-[40px]">
{isDefaultModel(model.fullName) && (
<Badge
className={cn(
'rounded-md border-0 px-2 py-1 font-normal capitalize',
'bg-emerald-900 text-emerald-400',
)}
variant="outline"
>
{t('common.recommended')}
</Badge>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<Badge
className={cn(
'border-brand ml-2 flex inline-flex h-5 w-5 items-center justify-center rounded-full border p-0 font-medium',
)}
variant="gradient"
>
<StarIcon className="text-brand size-3" />
</Badge>
</TooltipTrigger>
<TooltipPortal>
<TooltipContent align="center" side="top">
{t('common.recommended')}
</TooltipContent>
</TooltipPortal>
</Tooltip>
</TooltipProvider>
)}
</div>
<CardDescription className="h-full overflow-hidden text-ellipsis">
</CardTitle>
<CardDescription className="overflow-hidden text-ellipsis">
{model.description}
</CardDescription>
<div className="absolute right-3 top-3 flex items-center justify-center">
<OllamaModelInstallButton model={model.fullName} />
</div>
</CardHeader>
<CardContent className="flex flex-col space-y-1 text-xs">
<div className="flex h-[75px] flex-col space-y-1">
<CardContent className="flex flex-col items-center gap-1 text-xs">
<div className="flex flex-wrap items-center gap-2">
{model.capabilities.map((capability) => (
<ModelCapabilityTag
capability={capability}
key={capability}
/>
))}
<ModelQuailityTag quality={model.quality} />
<ModelSpeedTag speed={model.speed} />
<Badge variant="tags">
<BookOpenText className="h-3.5 w-3.5" />
<span className="ml-2 overflow-hidden text-ellipsis">
{t('shinkaiNode.models.labels.bookPages', {
pages: Math.round(
(model.contextLength * 0.75) / 380,
),
})}
</span>
</Badge>
<Badge variant="tags">
<Database className="mr-2 h-4 w-4" />
<span className="text-ellipsis">{model.size} GB</span>
</Badge>
</div>
<div className="pb-2 pt-0">
<Separator />
</div>
<ModelQuailityTag quality={model.quality} />
<ModelSpeedTag speed={model.speed} />
<Badge
className={cn(
'justify-center rounded-full px-2 py-1 font-normal capitalize',
)}
variant="outline"
>
<BookOpenText className="h-4 w-4" />
<span className="ml-2 overflow-hidden text-ellipsis">
{t('shinkaiNode.models.labels.bookPages', {
pages: Math.round((model.contextLength * 0.75) / 380),
})}
</span>
</Badge>
<Badge
className={cn(
'justify-center rounded-full px-2 py-1 font-normal capitalize',
)}
variant="outline"
>
<Database className="mr-2 h-4 w-4" />
<span className="ml-2 overflow-hidden text-ellipsis">
{model.size} GB
</span>
</Badge>
</CardContent>
<CardFooter className="flex h-[75px] flex-row items-center justify-center">
<OllamaModelInstallButton model={model.fullName} />
</CardFooter>
</Card>
);
})}
Expand All @@ -154,24 +177,20 @@ export const OllamaModels = () => {
</AutoSizer>
</div>
)}
<span className="text-gray-80 w-full text-right text-xs">
{t('shinkaiNode.models.poweredByOllama')}
</span>

<Button
className="gap-2 underline"
onClick={async () => setShowAllOllamaModels(!showAllOllamaModels)}
variant={'outline'}
variant={'link'}
>
{!showAllOllamaModels ? null : <Star className="ml-2 h-4 w-4" />}
{!showAllOllamaModels
? t('shinkaiNode.models.labels.showAll')
: t('shinkaiNode.models.labels.showRecommended')}
{!showAllOllamaModels ? (
<List className="ml-2" />
) : (
<Star className="ml-2" />
)}
</Button>

<span className="text-gray-80 justify-self-center text-xs">
{t('shinkaiNode.models.poweredByOllama')}
</span>
</div>
);
};
Loading

0 comments on commit aa73c5f

Please sign in to comment.