NamH ec9b5bf682
fix: update new api from cortex to support 0.5.0 (#3221)
* fix: update new api from cortex to support 0.5.0

Signed-off-by: James <namnh0122@gmail.com>

* fix stop button for streaming

Signed-off-by: James <namnh0122@gmail.com>

* fix stop inference for nonstreaming

Signed-off-by: James <namnh0122@gmail.com>

* chore: remove umami prevent tracking call to vercel

Signed-off-by: James <namnh0122@gmail.com>

* add warning modal when running more than 2 model concurrently

Signed-off-by: James <namnh0122@gmail.com>

* fix: skip summarize if abort

Signed-off-by: James <namnh0122@gmail.com>

* 0.5.0-3

* add inference error popup

Signed-off-by: James <namnh0122@gmail.com>

* add back import local model

Signed-off-by: James <namnh0122@gmail.com>

* fix: max token issue (#3225)

Signed-off-by: James <namnh0122@gmail.com>

* format status

Signed-off-by: James <namnh0122@gmail.com>

* fix migration missing instructions

Signed-off-by: James <namnh0122@gmail.com>

* fix: wait for cortex process overlay should be on top (#3224)

* fix: wait for cortex process overlay should be on top

* chore: update cortex.js

* Cortex 0.5.0-5

* add import model to my model screen

Signed-off-by: James <namnh0122@gmail.com>

* fix: should migrate symlink models (#3226)

* fix import on windows (#3229)

Signed-off-by: James <namnh0122@gmail.com>

* fix yarn lint

Signed-off-by: James <namnh0122@gmail.com>

* fix: clean up port before start jan (#3232)

Signed-off-by: James <namnh0122@gmail.com>

---------

Signed-off-by: James <namnh0122@gmail.com>
Co-authored-by: Van Pham <64197333+Van-QA@users.noreply.github.com>
Co-authored-by: Louis <louis@jan.ai>
2024-08-02 09:37:04 +07:00

204 lines
6.7 KiB
TypeScript

import { memo, useCallback, useMemo, useState } from 'react'
import { LocalEngines, Model } from '@janhq/core'
import { Badge, Button, useClickOutside } from '@janhq/joi'
import { useAtomValue, useSetAtom } from 'jotai'
import {
MoreVerticalIcon,
PlayIcon,
StopCircleIcon,
Trash2Icon,
} from 'lucide-react'
import { twMerge } from 'tailwind-merge'
import useModelStart from '@/hooks/useModelStart'
import useModelStop from '@/hooks/useModelStop'
import useModels from '@/hooks/useModels'
import { showWarningMultipleModelModalAtom } from '@/screens/HubScreen2/components/WarningMultipleModelModal'
import { activeModelsAtom } from '@/helpers/atoms/Model.atom'
type Props = {
model: Model
}
// If more than this number of models are running, show a warning modal.
export const concurrentModelWarningThreshold = 2
const ModelItem: React.FC<Props> = ({ model }) => {
const activeModels = useAtomValue(activeModelsAtom)
const startModel = useModelStart()
const stopModel = useModelStop()
const [more, setMore] = useState(false)
const { deleteModel } = useModels()
const [menu, setMenu] = useState<HTMLDivElement | null>(null)
const [toggle, setToggle] = useState<HTMLDivElement | null>(null)
const setShowWarningMultipleModelModal = useSetAtom(
showWarningMultipleModelModalAtom
)
useClickOutside(() => setMore(false), null, [menu, toggle])
const isActive = useMemo(
() => activeModels.map((m) => m.model).includes(model.model),
[activeModels, model.model]
)
const onModelActionClick = useCallback(
(modelId: string) => {
if (isActive) {
// if model already active, stop it
stopModel.mutate(modelId)
return
}
if (activeModels.length >= concurrentModelWarningThreshold) {
// if max concurrent models reached, stop the first model
// display popup
setShowWarningMultipleModelModal(true)
}
startModel.mutate(modelId)
},
[
isActive,
startModel,
stopModel,
activeModels.length,
setShowWarningMultipleModelModal,
]
)
const onDeleteModelClicked = useCallback(
async (modelId: string) => {
await stopModel.mutateAsync(modelId)
await deleteModel(modelId)
},
[stopModel, deleteModel]
)
const isLocalModel = LocalEngines.find(
(e) => model.engine != null && e === model.engine
)
return (
<div className="border border-b-0 border-[hsla(var(--app-border))] bg-[hsla(var(--tertiary-bg))] p-4 first:rounded-t-lg last:rounded-b-lg last:border-b">
<div className="flex flex-col items-start justify-start gap-4 sm:flex-row sm:items-center sm:justify-between">
<div className="flex w-1/2 gap-x-8">
<div className="flex w-full items-center justify-between">
<h6
className={twMerge(
'line-clamp-1 max-w-[200px] font-medium',
model.engine !== 'cortex.llamacpp' &&
'max-w-none text-[hsla(var(--text-secondary))]'
)}
title={model.model}
>
{model.model}
</h6>
{model.engine === 'cortex.llamacpp' && (
<div className="flex gap-x-8">
<p
className="line-clamp-1 max-w-[120px] text-[hsla(var(--text-secondary))] xl:max-w-none"
title={model.model}
>
{model.model}
</p>
</div>
)}
</div>
</div>
{isLocalModel && (
<div className="flex gap-x-4">
<Badge theme="secondary" className="sm:mr-16">
{model.version != null ? `v${model.version}` : '-'}
</Badge>
<div className="relative flex items-center gap-x-4">
{isActive ? (
<Badge
theme="success"
variant="soft"
className="inline-flex items-center space-x-2"
>
<span className="h-2 w-2 rounded-full bg-green-500" />
<span>Active</span>
</Badge>
) : (
<Badge
theme="secondary"
className="inline-flex items-center space-x-2"
>
<span className="h-2 w-2 rounded-full bg-gray-500" />
<span>Inactive</span>
</Badge>
)}
<div
className="inline-flex cursor-pointer"
ref={setToggle}
onClick={() => {
setMore(!more)
}}
>
<Button theme="icon">
<MoreVerticalIcon />
</Button>
{more && (
<div
className="shadow-lg absolute right-8 top-0 z-20 w-52 overflow-hidden rounded-lg border border-[hsla(var(--app-border))] bg-[hsla(var(--app-bg))]"
ref={setMenu}
>
<div
className={twMerge(
'flex items-center space-x-2 px-4 py-2 hover:bg-[hsla(var(--dropdown-menu-hover-bg))]'
)}
onClick={() => {
onModelActionClick(model.model)
setMore(false)
}}
>
{isActive ? (
<StopCircleIcon
size={16}
className="text-[hsla(var(--text-secondary))]"
/>
) : (
<PlayIcon
size={16}
className="text-[hsla(var(--text-secondary))]"
/>
)}
<span className="text-bold capitalize">
{isActive ? 'Stop' : 'Start'}
&nbsp;Model
</span>
</div>
<div
className={twMerge(
'flex cursor-pointer items-center space-x-2 px-4 py-2 hover:bg-[hsla(var(--dropdown-menu-hover-bg))]'
)}
onClick={() => onDeleteModelClicked(model.model)}
>
<Trash2Icon
size={16}
className="text-[hsla(var(--destructive-bg))]"
/>
<span className="text-bold text-[hsla(var(--destructive-bg))]">
Delete Model
</span>
</div>
</div>
)}
</div>
</div>
</div>
)}
</div>
</div>
)
}
export default memo(ModelItem)