chore: update model handlers on the new frontend (#5011)
* chore: provide model handlers to new frontend * chore: add API server function to the new front end
This commit is contained in:
parent
74c2c59c90
commit
2345ff172d
@ -5,12 +5,15 @@ import {
|
|||||||
} from '@/components/ui/popover'
|
} from '@/components/ui/popover'
|
||||||
import { Progress } from '@/components/ui/progress'
|
import { Progress } from '@/components/ui/progress'
|
||||||
import { useDownloadStore } from '@/hooks/useDownloadStore'
|
import { useDownloadStore } from '@/hooks/useDownloadStore'
|
||||||
|
import { useModelProvider } from '@/hooks/useModelProvider'
|
||||||
import { abortDownload } from '@/services/models'
|
import { abortDownload } from '@/services/models'
|
||||||
|
import { getProviders } from '@/services/providers'
|
||||||
import { DownloadEvent, DownloadState, events } from '@janhq/core'
|
import { DownloadEvent, DownloadState, events } from '@janhq/core'
|
||||||
import { IconX } from '@tabler/icons-react'
|
import { IconX } from '@tabler/icons-react'
|
||||||
import { useCallback, useEffect, useMemo } from 'react'
|
import { useCallback, useEffect, useMemo } from 'react'
|
||||||
|
|
||||||
export function DownloadManagement() {
|
export function DownloadManagement() {
|
||||||
|
const { setProviders } = useModelProvider()
|
||||||
const { downloads, updateProgress, removeDownload } = useDownloadStore()
|
const { downloads, updateProgress, removeDownload } = useDownloadStore()
|
||||||
const downloadCount = useMemo(
|
const downloadCount = useMemo(
|
||||||
() => Object.keys(downloads).length,
|
() => Object.keys(downloads).length,
|
||||||
@ -72,8 +75,9 @@ export function DownloadManagement() {
|
|||||||
async (state: DownloadState) => {
|
async (state: DownloadState) => {
|
||||||
console.debug('onFileDownloadSuccess', state)
|
console.debug('onFileDownloadSuccess', state)
|
||||||
removeDownload(state.modelId)
|
removeDownload(state.modelId)
|
||||||
|
getProviders().then(setProviders)
|
||||||
},
|
},
|
||||||
[removeDownload]
|
[removeDownload, setProviders]
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@ -9,22 +9,37 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from '@/components/ui/dialog'
|
} from '@/components/ui/dialog'
|
||||||
|
import { useModelProvider } from '@/hooks/useModelProvider'
|
||||||
|
import { deleteModel } from '@/services/models'
|
||||||
|
import { getProviders } from '@/services/providers'
|
||||||
|
|
||||||
import { IconTrash } from '@tabler/icons-react'
|
import { IconTrash } from '@tabler/icons-react'
|
||||||
|
|
||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
|
|
||||||
type DialoDeleteModelProps = {
|
type DialogDeleteModelProps = {
|
||||||
provider: ModelProvider
|
provider: ModelProvider
|
||||||
modelId?: string
|
modelId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DialoDeleteModel = ({
|
export const DialogDeleteModel = ({
|
||||||
provider,
|
provider,
|
||||||
modelId,
|
modelId,
|
||||||
}: DialoDeleteModelProps) => {
|
}: DialogDeleteModelProps) => {
|
||||||
const [selectedModelId, setSelectedModelId] = useState<string>('')
|
const [selectedModelId, setSelectedModelId] = useState<string>('')
|
||||||
|
const { setProviders, deleteModel: deleteModelCache } = useModelProvider()
|
||||||
|
|
||||||
|
const removeModel = async () => {
|
||||||
|
deleteModelCache(selectedModelId)
|
||||||
|
deleteModel(selectedModelId).then(() => {
|
||||||
|
getProviders().then(setProviders)
|
||||||
|
toast.success('Delete Model', {
|
||||||
|
id: `delete-model-${selectedModel?.id}`,
|
||||||
|
description: `Model ${selectedModel?.id} has been permanently deleted.`,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize with the provided model ID or the first model if available
|
// Initialize with the provided model ID or the first model if available
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -68,16 +83,7 @@ export const DialoDeleteModel = ({
|
|||||||
</Button>
|
</Button>
|
||||||
</DialogClose>
|
</DialogClose>
|
||||||
<DialogClose asChild>
|
<DialogClose asChild>
|
||||||
<Button
|
<Button variant="destructive" size="sm" onClick={removeModel}>
|
||||||
variant="destructive"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => {
|
|
||||||
toast.success('Delete Model', {
|
|
||||||
id: `delete-model-${selectedModel.id}`,
|
|
||||||
description: `Model ${selectedModel.id} has been permanently deleted.`,
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Delete
|
Delete
|
||||||
</Button>
|
</Button>
|
||||||
</DialogClose>
|
</DialogClose>
|
||||||
|
|||||||
@ -21,6 +21,9 @@ type LocalApiServerState = {
|
|||||||
// Verbose server logs
|
// Verbose server logs
|
||||||
verboseLogs: boolean
|
verboseLogs: boolean
|
||||||
setVerboseLogs: (value: boolean) => void
|
setVerboseLogs: (value: boolean) => void
|
||||||
|
// Server status
|
||||||
|
serverStatus: 'running' | 'stopped' | 'pending'
|
||||||
|
setServerStatus: (value: 'running' | 'stopped' | 'pending') => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useLocalApiServer = create<LocalApiServerState>()(
|
export const useLocalApiServer = create<LocalApiServerState>()(
|
||||||
@ -38,6 +41,8 @@ export const useLocalApiServer = create<LocalApiServerState>()(
|
|||||||
setCorsEnabled: (value) => set({ corsEnabled: value }),
|
setCorsEnabled: (value) => set({ corsEnabled: value }),
|
||||||
verboseLogs: true,
|
verboseLogs: true,
|
||||||
setVerboseLogs: (value) => set({ verboseLogs: value }),
|
setVerboseLogs: (value) => set({ verboseLogs: value }),
|
||||||
|
serverStatus: 'stopped',
|
||||||
|
setServerStatus: (value) => set({ serverStatus: value }),
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: localStoregeKey.settingLocalApiServer,
|
name: localStoregeKey.settingLocalApiServer,
|
||||||
|
|||||||
@ -13,6 +13,7 @@ type ModelProviderState = {
|
|||||||
providerName: string,
|
providerName: string,
|
||||||
modelName: string
|
modelName: string
|
||||||
) => Model | undefined
|
) => Model | undefined
|
||||||
|
deleteModel: (modelId: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useModelProvider = create<ModelProviderState>()(
|
export const useModelProvider = create<ModelProviderState>()(
|
||||||
@ -31,7 +32,9 @@ export const useModelProvider = create<ModelProviderState>()(
|
|||||||
const models = existingProvider?.models || []
|
const models = existingProvider?.models || []
|
||||||
const mergedModels = [
|
const mergedModels = [
|
||||||
...(provider?.models ?? []),
|
...(provider?.models ?? []),
|
||||||
...models.filter((e) => !provider?.models.some((m) => m.id === e.id)),
|
...models.filter(
|
||||||
|
(e) => !provider?.models.some((m) => m.id === e.id)
|
||||||
|
),
|
||||||
]
|
]
|
||||||
return {
|
return {
|
||||||
...provider,
|
...provider,
|
||||||
@ -98,6 +101,19 @@ export const useModelProvider = create<ModelProviderState>()(
|
|||||||
|
|
||||||
return modelObject
|
return modelObject
|
||||||
},
|
},
|
||||||
|
deleteModel: (modelId: string) => {
|
||||||
|
set((state) => ({
|
||||||
|
providers: state.providers.map((provider) => {
|
||||||
|
const models = provider.models.filter(
|
||||||
|
(model) => model.id !== modelId
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
...provider,
|
||||||
|
models,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: localStoregeKey.modelProvider,
|
name: localStoregeKey.modelProvider,
|
||||||
|
|||||||
@ -19,8 +19,44 @@ export const Route = createFileRoute(route.settings.local_api_server as any)({
|
|||||||
|
|
||||||
function LocalAPIServer() {
|
function LocalAPIServer() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { corsEnabled, setCorsEnabled, verboseLogs, setVerboseLogs } =
|
const {
|
||||||
useLocalApiServer()
|
corsEnabled,
|
||||||
|
setCorsEnabled,
|
||||||
|
verboseLogs,
|
||||||
|
setVerboseLogs,
|
||||||
|
serverHost,
|
||||||
|
serverPort,
|
||||||
|
apiPrefix,
|
||||||
|
serverStatus,
|
||||||
|
setServerStatus,
|
||||||
|
} = useLocalApiServer()
|
||||||
|
|
||||||
|
const toggleAPIServer = async () => {
|
||||||
|
setServerStatus('pending')
|
||||||
|
if (serverStatus === 'stopped') {
|
||||||
|
window.core?.api
|
||||||
|
?.startServer({
|
||||||
|
host: serverHost,
|
||||||
|
port: serverPort,
|
||||||
|
prefix: apiPrefix,
|
||||||
|
isCorsEnabled: corsEnabled,
|
||||||
|
isVerboseEnabled: verboseLogs,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
setServerStatus('running')
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
window.core?.api
|
||||||
|
?.stopServer()
|
||||||
|
.then(() => {
|
||||||
|
setServerStatus('stopped')
|
||||||
|
})
|
||||||
|
.catch((error: unknown) => {
|
||||||
|
console.error('Error stopping server:', error)
|
||||||
|
setServerStatus('stopped')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleOpenLogs = async () => {
|
const handleOpenLogs = async () => {
|
||||||
try {
|
try {
|
||||||
@ -78,7 +114,10 @@ function LocalAPIServer() {
|
|||||||
Start an OpenAI-compatible local HTTP server.
|
Start an OpenAI-compatible local HTTP server.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Button>Start Server</Button>
|
<Button onClick={toggleAPIServer}>
|
||||||
|
{`${serverStatus === 'running' ? 'Stop' : 'Start'}`}{' '}
|
||||||
|
Server
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -18,7 +18,7 @@ import { RenderMarkdown } from '@/containers/RenderMarkdown'
|
|||||||
import { DialogEditModel } from '@/containers/dialogs/EditModel'
|
import { DialogEditModel } from '@/containers/dialogs/EditModel'
|
||||||
import { DialogAddModel } from '@/containers/dialogs/AddModel'
|
import { DialogAddModel } from '@/containers/dialogs/AddModel'
|
||||||
import { ModelSetting } from '@/containers/ModelSetting'
|
import { ModelSetting } from '@/containers/ModelSetting'
|
||||||
import { DialoDeleteModel } from '@/containers/dialogs/DeleteModel'
|
import { DialogDeleteModel } from '@/containers/dialogs/DeleteModel'
|
||||||
import Joyride, { CallBackProps, STATUS } from 'react-joyride'
|
import Joyride, { CallBackProps, STATUS } from 'react-joyride'
|
||||||
import { CustomTooltipJoyRide } from '@/containers/CustomeTooltipJoyRide'
|
import { CustomTooltipJoyRide } from '@/containers/CustomeTooltipJoyRide'
|
||||||
import { route } from '@/constants/routes'
|
import { route } from '@/constants/routes'
|
||||||
@ -250,7 +250,7 @@ function ProviderDetail() {
|
|||||||
{model.settings && (
|
{model.settings && (
|
||||||
<ModelSetting provider={provider} model={model} />
|
<ModelSetting provider={provider} model={model} />
|
||||||
)}
|
)}
|
||||||
<DialoDeleteModel
|
<DialogDeleteModel
|
||||||
provider={provider}
|
provider={provider}
|
||||||
modelId={model.id}
|
modelId={model.id}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -35,6 +35,16 @@ export const fetchModelSources = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the model hub.
|
||||||
|
* @returns A promise that resolves to the model hub.
|
||||||
|
*/
|
||||||
|
export const fetchModelHub = async () => {
|
||||||
|
return ExtensionManager.getInstance()
|
||||||
|
.get<ModelExtension>(ExtensionTypeEnum.Model)
|
||||||
|
?.fetchModelsHub()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new model source.
|
* Adds a new model source.
|
||||||
* @param source The source to add.
|
* @param source The source to add.
|
||||||
@ -137,3 +147,23 @@ export const abortDownload = async (id: string) => {
|
|||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a model.
|
||||||
|
* @param id
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const deleteModel = async (id: string) => {
|
||||||
|
const extension = ExtensionManager.getInstance().get<ModelExtension>(
|
||||||
|
ExtensionTypeEnum.Model
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!extension) throw new Error('Model extension not found')
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await extension.deleteModel(id)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to delete model:', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
import { models as providerModels } from 'token.js'
|
import { models as providerModels } from 'token.js'
|
||||||
import { mockModelProvider } from '@/mock/data'
|
import { mockModelProvider } from '@/mock/data'
|
||||||
import { EngineManager, ModelManager } from '@janhq/core'
|
import { EngineManager } from '@janhq/core'
|
||||||
import { ModelCapabilities } from '@/types/models'
|
import { ModelCapabilities } from '@/types/models'
|
||||||
import { modelSettings } from '@/lib/predefined'
|
import { modelSettings } from '@/lib/predefined'
|
||||||
|
import { fetchModels } from './models'
|
||||||
|
|
||||||
export const getProviders = async (): Promise<ModelProvider[]> => {
|
export const getProviders = async (): Promise<ModelProvider[]> => {
|
||||||
const builtinProviders = mockModelProvider.map((provider) => {
|
const builtinProviders = mockModelProvider.map((provider) => {
|
||||||
@ -42,8 +43,9 @@ export const getProviders = async (): Promise<ModelProvider[]> => {
|
|||||||
for (const [key, value] of EngineManager.instance().engines) {
|
for (const [key, value] of EngineManager.instance().engines) {
|
||||||
// TODO: Remove this when the cortex extension is removed
|
// TODO: Remove this when the cortex extension is removed
|
||||||
const providerName = key === 'cortex' ? 'llama.cpp' : key
|
const providerName = key === 'cortex' ? 'llama.cpp' : key
|
||||||
|
|
||||||
const models =
|
const models =
|
||||||
Array.from(ModelManager.instance().models.values()).filter(
|
((await fetchModels()) ?? []).filter(
|
||||||
(model) =>
|
(model) =>
|
||||||
(model.engine === 'llama-cpp' ? 'llama.cpp' : model.engine) ===
|
(model.engine === 'llama-cpp' ? 'llama.cpp' : model.engine) ===
|
||||||
providerName &&
|
providerName &&
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user