jan/web-app/src/containers/ModelSupportStatus.tsx
2025-08-28 13:10:08 +07:00

177 lines
5.2 KiB
TypeScript

import { useCallback, useEffect, useState } from 'react'
import { cn } from '@/lib/utils'
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from '@/components/ui/tooltip'
import { isModelSupported } from '@/services/models'
import { getJanDataFolderPath, joinPath, fs } from '@janhq/core'
import { invoke } from '@tauri-apps/api/core'
interface ModelSupportStatusProps {
modelId: string | undefined
provider: string | undefined
contextSize: number
className?: string
}
export const ModelSupportStatus = ({
modelId,
provider,
contextSize,
className,
}: ModelSupportStatusProps) => {
const [modelSupportStatus, setModelSupportStatus] = useState<
'RED' | 'YELLOW' | 'GREEN' | 'LOADING' | null | 'GREY'
>(null)
// Helper function to check model support with proper path resolution
const checkModelSupportWithPath = useCallback(
async (
id: string,
ctxSize: number
): Promise<'RED' | 'YELLOW' | 'GREEN' | 'GREY' | null> => {
try {
const janDataFolder = await getJanDataFolderPath()
// First try the standard downloaded model path
const ggufModelPath = await joinPath([
janDataFolder,
'llamacpp',
'models',
id,
'model.gguf',
])
// Check if the standard model.gguf file exists
if (await fs.existsSync(ggufModelPath)) {
return await isModelSupported(ggufModelPath, ctxSize)
}
// If model.gguf doesn't exist, try reading from model.yml (for imported models)
const modelConfigPath = await joinPath([
janDataFolder,
'llamacpp',
'models',
id,
'model.yml',
])
if (!(await fs.existsSync(modelConfigPath))) {
console.error(
`Neither model.gguf nor model.yml found for model: ${id}`
)
return null
}
// Read the model configuration to get the actual model path
const modelConfig = await invoke<{ model_path: string }>('read_yaml', {
path: `llamacpp/models/${id}/model.yml`,
})
// Handle both absolute and relative paths
const actualModelPath =
modelConfig.model_path.startsWith('/') ||
modelConfig.model_path.match(/^[A-Za-z]:/)
? modelConfig.model_path // absolute path, use as-is
: await joinPath([janDataFolder, modelConfig.model_path]) // relative path, join with data folder
return await isModelSupported(actualModelPath, ctxSize)
} catch (error) {
console.error(
'Error checking model support with path resolution:',
error
)
// If path construction or model support check fails, assume not supported
return null
}
},
[]
)
// Helper function to get icon color based on model support status
const getStatusColor = (): string => {
switch (modelSupportStatus) {
case 'GREEN':
return 'bg-green-500'
case 'YELLOW':
return 'bg-yellow-500'
case 'RED':
return 'bg-red-500'
case 'LOADING':
return 'bg-main-view-fg/50'
default:
return 'bg-main-view-fg/50'
}
}
// Helper function to get tooltip text based on model support status
const getStatusTooltip = (): string => {
switch (modelSupportStatus) {
case 'GREEN':
return `Works Well on your device (ctx: ${contextSize})`
case 'YELLOW':
return `Might work on your device (ctx: ${contextSize})`
case 'RED':
return `Doesn't work on your device (ctx: ${contextSize})`
case 'LOADING':
return 'Checking device compatibility...'
default:
return 'Unknown'
}
}
// Check model support when model changes
useEffect(() => {
const checkModelSupport = async () => {
if (modelId && provider === 'llamacpp') {
// Set loading state immediately
setModelSupportStatus('LOADING')
try {
const supportStatus = await checkModelSupportWithPath(
modelId,
contextSize
)
setModelSupportStatus(supportStatus)
} catch (error) {
console.error('Error checking model support:', error)
setModelSupportStatus('RED')
}
} else {
// Only show status for llamacpp models since isModelSupported is specific to llamacpp
setModelSupportStatus(null)
}
}
checkModelSupport()
}, [modelId, provider, contextSize, checkModelSupportWithPath])
// Don't render anything if no status or not llamacpp
if (!modelSupportStatus || provider !== 'llamacpp') {
return null
}
return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div
className={cn(
'size-2 flex items-center justify-center rounded-full',
modelSupportStatus === 'LOADING'
? 'size-2.5 border border-main-view-fg/50 border-t-transparent animate-spin'
: getStatusColor(),
className
)}
/>
</TooltipTrigger>
<TooltipContent>
<p>{getStatusTooltip()}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)
}