feat: allow users to refresh cloud model list (#4698)
* feat: allow users to refresh cloud model list * chore: reusable model list refresh * chore: clean up
This commit is contained in:
parent
046e8d5094
commit
eba6884abb
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jan",
|
"name": "jan",
|
||||||
"version": "0.1.1737985524",
|
"version": "0.1.1",
|
||||||
"main": "./build/main.js",
|
"main": "./build/main.js",
|
||||||
"author": "Jan <service@jan.ai>",
|
"author": "Jan <service@jan.ai>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
1.0.10
|
1.0.11-rc1
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { motion } from 'framer-motion'
|
import { motion } from 'framer-motion'
|
||||||
|
|
||||||
const Spinner = ({ size = 40, strokeWidth = 4 }) => {
|
const Spinner = ({ size = 40, strokeWidth = 4, className = '' }) => {
|
||||||
const radius = size / 2 - strokeWidth
|
const radius = size / 2 - strokeWidth
|
||||||
const circumference = 2 * Math.PI * radius
|
const circumference = 2 * Math.PI * radius
|
||||||
|
|
||||||
@ -11,6 +11,7 @@ const Spinner = ({ size = 40, strokeWidth = 4 }) => {
|
|||||||
viewBox={`0 0 ${size} ${size}`}
|
viewBox={`0 0 ${size} ${size}`}
|
||||||
style={{ overflow: 'visible' }}
|
style={{ overflow: 'visible' }}
|
||||||
animate={{ rotate: 360 }}
|
animate={{ rotate: 360 }}
|
||||||
|
className={className}
|
||||||
transition={{
|
transition={{
|
||||||
repeat: Infinity,
|
repeat: Infinity,
|
||||||
duration: 2, // Adjust for desired speed
|
duration: 2, // Adjust for desired speed
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useMemo } from 'react'
|
import { useCallback, useMemo, useState } from 'react'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ExtensionTypeEnum,
|
ExtensionTypeEnum,
|
||||||
@ -32,6 +32,13 @@ export const releasedEnginesLatestCacheAtom = atomWithStorage<{
|
|||||||
timestamp: number
|
timestamp: number
|
||||||
} | null>('releasedEnginesLatestCache', null, undefined, { getOnInit: true })
|
} | null>('releasedEnginesLatestCache', null, undefined, { getOnInit: true })
|
||||||
|
|
||||||
|
export interface RemoteModelList {
|
||||||
|
data?: {
|
||||||
|
id?: string
|
||||||
|
name?: string
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
// fetcher function
|
// fetcher function
|
||||||
async function fetchExtensionData<T>(
|
async function fetchExtensionData<T>(
|
||||||
extension: EngineManagementExtension | null,
|
extension: EngineManagementExtension | null,
|
||||||
@ -88,8 +95,12 @@ export function useGetRemoteModels(name: string) {
|
|||||||
error,
|
error,
|
||||||
mutate,
|
mutate,
|
||||||
} = useSWR(
|
} = useSWR(
|
||||||
extension ? 'remoteModels' : null,
|
extension ? `remoteModels_${name}` : null,
|
||||||
() => fetchExtensionData(extension, (ext) => ext.getRemoteModels(name)),
|
() =>
|
||||||
|
fetchExtensionData(
|
||||||
|
extension,
|
||||||
|
(ext) => ext.getRemoteModels(name) as Promise<RemoteModelList>
|
||||||
|
),
|
||||||
{
|
{
|
||||||
revalidateOnFocus: false,
|
revalidateOnFocus: false,
|
||||||
revalidateOnReconnect: true,
|
revalidateOnReconnect: true,
|
||||||
@ -456,3 +467,30 @@ export const useGetEngineModelSources = () => {
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh model list
|
||||||
|
* @param engine
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const useRefreshModelList = (engine: string) => {
|
||||||
|
const [refreshingModels, setRefreshingModels] = useState(false)
|
||||||
|
const { mutate: fetchRemoteModels } = useGetRemoteModels(engine)
|
||||||
|
|
||||||
|
const refreshModels = useCallback(() => {
|
||||||
|
setRefreshingModels(true)
|
||||||
|
fetchRemoteModels()
|
||||||
|
.then((remoteModelList) =>
|
||||||
|
Promise.all(
|
||||||
|
remoteModelList?.data?.map((model: { id?: string }) =>
|
||||||
|
model?.id
|
||||||
|
? addRemoteEngineModel(model.id, engine).catch(() => {})
|
||||||
|
: {}
|
||||||
|
) ?? []
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.finally(() => setRefreshingModels(false))
|
||||||
|
}, [fetchRemoteModels])
|
||||||
|
|
||||||
|
return { refreshingModels, refreshModels }
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@janhq/web",
|
"name": "@janhq/web",
|
||||||
"version": "0.5.13",
|
"version": "0.5.15",
|
||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "./",
|
"homepage": "./",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -7,13 +7,17 @@ import {
|
|||||||
ArrowLeftIcon,
|
ArrowLeftIcon,
|
||||||
DownloadIcon,
|
DownloadIcon,
|
||||||
FileJson,
|
FileJson,
|
||||||
|
RefreshCwIcon,
|
||||||
SettingsIcon,
|
SettingsIcon,
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
|
|
||||||
|
import Spinner from '@/containers/Loader/Spinner'
|
||||||
import ModelDownloadButton from '@/containers/ModelDownloadButton'
|
import ModelDownloadButton from '@/containers/ModelDownloadButton'
|
||||||
|
|
||||||
import { MainViewState } from '@/constants/screens'
|
import { MainViewState } from '@/constants/screens'
|
||||||
|
|
||||||
|
import { useRefreshModelList } from '@/hooks/useEngineManagement'
|
||||||
|
|
||||||
import { MarkdownTextMessage } from '@/screens/Thread/ThreadCenterPanel/TextMessage/MarkdownTextMessage'
|
import { MarkdownTextMessage } from '@/screens/Thread/ThreadCenterPanel/TextMessage/MarkdownTextMessage'
|
||||||
|
|
||||||
import { toGigabytes } from '@/utils/converter'
|
import { toGigabytes } from '@/utils/converter'
|
||||||
@ -30,6 +34,8 @@ type Props = {
|
|||||||
const ModelPage = ({ model, onGoBack }: Props) => {
|
const ModelPage = ({ model, onGoBack }: Props) => {
|
||||||
const setSelectedSetting = useSetAtom(selectedSettingAtom)
|
const setSelectedSetting = useSetAtom(selectedSettingAtom)
|
||||||
const setMainViewState = useSetAtom(mainViewStateAtom)
|
const setMainViewState = useSetAtom(mainViewStateAtom)
|
||||||
|
const { refreshingModels, refreshModels } = useRefreshModelList(model.id)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollArea data-testid="hub-container-test-id" className="h-full w-full">
|
<ScrollArea data-testid="hub-container-test-id" className="h-full w-full">
|
||||||
<div className="flex h-full w-full justify-center">
|
<div className="flex h-full w-full justify-center">
|
||||||
@ -127,7 +133,7 @@ const ModelPage = ({ model, onGoBack }: Props) => {
|
|||||||
<table className="w-full p-4">
|
<table className="w-full p-4">
|
||||||
<thead className="bg-[hsla(var(--tertiary-bg))]">
|
<thead className="bg-[hsla(var(--tertiary-bg))]">
|
||||||
<tr>
|
<tr>
|
||||||
<th className="flex-1 px-6 py-3 text-left text-sm font-semibold">
|
<th className="flex flex-1 flex-row items-center justify-between px-6 py-3 text-left text-sm font-semibold">
|
||||||
{model.type !== 'cloud' ? 'Version' : 'Models'}
|
{model.type !== 'cloud' ? 'Version' : 'Models'}
|
||||||
</th>
|
</th>
|
||||||
{model.type !== 'cloud' && (
|
{model.type !== 'cloud' && (
|
||||||
@ -140,7 +146,27 @@ const ModelPage = ({ model, onGoBack }: Props) => {
|
|||||||
</th>
|
</th>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<th className="w-[120px]"></th>
|
<th className="w-[120px]">
|
||||||
|
{model.type === 'cloud' && (
|
||||||
|
<Button
|
||||||
|
theme={'ghost'}
|
||||||
|
variant={'outline'}
|
||||||
|
className="h-7 px-2"
|
||||||
|
onClick={() => refreshModels()}
|
||||||
|
>
|
||||||
|
{refreshingModels ? (
|
||||||
|
<Spinner
|
||||||
|
size={16}
|
||||||
|
strokeWidth={2}
|
||||||
|
className="mr-2"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<RefreshCwIcon size={16} className="mr-2" />
|
||||||
|
)}
|
||||||
|
Refresh
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|||||||
@ -15,15 +15,27 @@ interface EngineConfig extends OriginalEngineConfig {
|
|||||||
[key: string]: any
|
[key: string]: any
|
||||||
}
|
}
|
||||||
|
|
||||||
import { ScrollArea, Input, TextArea } from '@janhq/joi'
|
import { ScrollArea, Input, TextArea, Button } from '@janhq/joi'
|
||||||
|
|
||||||
import { useAtomValue, useSetAtom } from 'jotai'
|
import { useAtomValue, useSetAtom } from 'jotai'
|
||||||
|
|
||||||
import { set } from 'lodash'
|
import { set } from 'lodash'
|
||||||
import { ChevronDown, ChevronRight, Eye, EyeOff } from 'lucide-react'
|
import {
|
||||||
|
ChevronDown,
|
||||||
|
ChevronRight,
|
||||||
|
Eye,
|
||||||
|
EyeOff,
|
||||||
|
RefreshCwIcon,
|
||||||
|
} from 'lucide-react'
|
||||||
import { twMerge } from 'tailwind-merge'
|
import { twMerge } from 'tailwind-merge'
|
||||||
|
|
||||||
import { updateEngine, useGetEngines } from '@/hooks/useEngineManagement'
|
import Spinner from '@/containers/Loader/Spinner'
|
||||||
|
|
||||||
|
import {
|
||||||
|
updateEngine,
|
||||||
|
useGetEngines,
|
||||||
|
useRefreshModelList,
|
||||||
|
} from '@/hooks/useEngineManagement'
|
||||||
|
|
||||||
import { getTitleByEngine } from '@/utils/modelEngine'
|
import { getTitleByEngine } from '@/utils/modelEngine'
|
||||||
|
|
||||||
@ -51,6 +63,7 @@ const RemoteEngineSettings = ({
|
|||||||
const setSelectedModel = useSetAtom(selectedModelAtom)
|
const setSelectedModel = useSetAtom(selectedModelAtom)
|
||||||
const customEngineLogo = getLogoEngine(name)
|
const customEngineLogo = getLogoEngine(name)
|
||||||
const threads = useAtomValue(threadsAtom)
|
const threads = useAtomValue(threadsAtom)
|
||||||
|
const { refreshingModels, refreshModels } = useRefreshModelList(name)
|
||||||
|
|
||||||
const engine =
|
const engine =
|
||||||
engines &&
|
engines &&
|
||||||
@ -214,8 +227,22 @@ const RemoteEngineSettings = ({
|
|||||||
<div>
|
<div>
|
||||||
<h6 className="mb-2 line-clamp-1 font-semibold">Model</h6>
|
<h6 className="mb-2 line-clamp-1 font-semibold">Model</h6>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Button
|
||||||
|
theme={'ghost'}
|
||||||
|
variant={'outline'}
|
||||||
|
onClick={() => refreshModels()}
|
||||||
|
>
|
||||||
|
{refreshingModels ? (
|
||||||
|
<Spinner size={16} strokeWidth={2} className="mr-2" />
|
||||||
|
) : (
|
||||||
|
<RefreshCwIcon size={16} className="mr-2" />
|
||||||
|
)}
|
||||||
|
Refresh
|
||||||
|
</Button>
|
||||||
<ModalAddModel engine={name} />
|
<ModalAddModel engine={name} />
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
{remoteModels &&
|
{remoteModels &&
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user