fix: search models result in hub should be sorted by weight (#5954)
This commit is contained in:
parent
812a8082b8
commit
160d158152
@ -43,6 +43,7 @@
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"culori": "^4.0.1",
|
||||
"emoji-picker-react": "^4.12.2",
|
||||
"fuse.js": "^7.1.0",
|
||||
"fzf": "^0.5.2",
|
||||
"i18next": "^25.0.1",
|
||||
"katex": "^0.16.22",
|
||||
|
||||
@ -3,7 +3,6 @@ import {
|
||||
getProviderLogo,
|
||||
getProviderTitle,
|
||||
getReadableLanguageName,
|
||||
fuzzySearch,
|
||||
toGigabytes,
|
||||
formatMegaBytes,
|
||||
formatDuration,
|
||||
@ -67,40 +66,6 @@ describe('getReadableLanguageName', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('fuzzySearch', () => {
|
||||
it('returns true for exact matches', () => {
|
||||
expect(fuzzySearch('hello', 'hello')).toBe(true)
|
||||
expect(fuzzySearch('test', 'test')).toBe(true)
|
||||
})
|
||||
|
||||
it('returns true for subsequence matches', () => {
|
||||
expect(fuzzySearch('hlo', 'hello')).toBe(true)
|
||||
expect(fuzzySearch('js', 'javascript')).toBe(true)
|
||||
expect(fuzzySearch('abc', 'aabbcc')).toBe(true)
|
||||
})
|
||||
|
||||
it('returns false when needle is longer than haystack', () => {
|
||||
expect(fuzzySearch('hello', 'hi')).toBe(false)
|
||||
expect(fuzzySearch('test', 'te')).toBe(false)
|
||||
})
|
||||
|
||||
it('returns false for non-matching patterns', () => {
|
||||
expect(fuzzySearch('xyz', 'hello')).toBe(false)
|
||||
expect(fuzzySearch('ba', 'abc')).toBe(false)
|
||||
})
|
||||
|
||||
it('handles empty strings', () => {
|
||||
expect(fuzzySearch('', '')).toBe(true)
|
||||
expect(fuzzySearch('', 'hello')).toBe(true)
|
||||
expect(fuzzySearch('h', '')).toBe(false)
|
||||
})
|
||||
|
||||
it('is case sensitive', () => {
|
||||
expect(fuzzySearch('H', 'hello')).toBe(false)
|
||||
expect(fuzzySearch('h', 'Hello')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('toGigabytes', () => {
|
||||
it('returns empty string for falsy inputs', () => {
|
||||
expect(toGigabytes(0)).toBe('')
|
||||
|
||||
@ -99,27 +99,6 @@ export const isLocalProvider = (provider: string) => {
|
||||
return extension && 'load' in extension
|
||||
}
|
||||
|
||||
export function fuzzySearch(needle: string, haystack: string) {
|
||||
const hlen = haystack.length
|
||||
const nlen = needle.length
|
||||
if (nlen > hlen) {
|
||||
return false
|
||||
}
|
||||
if (nlen === hlen) {
|
||||
return needle === haystack
|
||||
}
|
||||
outer: for (let i = 0, j = 0; i < nlen; i++) {
|
||||
const nch = needle.charCodeAt(i)
|
||||
while (j < hlen) {
|
||||
if (haystack.charCodeAt(j++) === nch) {
|
||||
continue outer
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
export const toGigabytes = (
|
||||
input: number,
|
||||
options?: { hideUnit?: boolean; toFixed?: number }
|
||||
|
||||
@ -3,7 +3,7 @@ import { useVirtualizer } from '@tanstack/react-virtual'
|
||||
import { createFileRoute, useNavigate, useSearch } from '@tanstack/react-router'
|
||||
import { route } from '@/constants/routes'
|
||||
import { useModelSources } from '@/hooks/useModelSources'
|
||||
import { cn, fuzzySearch } from '@/lib/utils'
|
||||
import { cn } from '@/lib/utils'
|
||||
import {
|
||||
useState,
|
||||
useMemo,
|
||||
@ -38,6 +38,7 @@ import { Progress } from '@/components/ui/progress'
|
||||
import HeaderPage from '@/containers/HeaderPage'
|
||||
import { Loader } from 'lucide-react'
|
||||
import { useTranslation } from '@/i18n/react-i18next-compat'
|
||||
import Fuse from 'fuse.js'
|
||||
|
||||
type ModelProps = {
|
||||
model: CatalogModel
|
||||
@ -62,6 +63,12 @@ function Hub() {
|
||||
{ value: 'newest', name: t('hub:sortNewest') },
|
||||
{ value: 'most-downloaded', name: t('hub:sortMostDownloaded') },
|
||||
]
|
||||
const searchOptions = {
|
||||
includeScore: true,
|
||||
// Search in `author` and in `tags` array
|
||||
keys: ['model_name', 'quants.model_id'],
|
||||
}
|
||||
|
||||
const { sources, addSource, fetchSources, loading } = useModelSources()
|
||||
const search = useSearch({ from: route.hub.index as any })
|
||||
const [searchValue, setSearchValue] = useState('')
|
||||
@ -177,24 +184,22 @@ function Hub() {
|
||||
})
|
||||
}, [sortSelected, sources])
|
||||
|
||||
// Filtered models
|
||||
// Filtered models (debounced search)
|
||||
const [debouncedSearchValue, setDebouncedSearchValue] = useState(searchValue)
|
||||
|
||||
useEffect(() => {
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedSearchValue(searchValue)
|
||||
}, 300)
|
||||
return () => clearTimeout(handler)
|
||||
}, [searchValue])
|
||||
|
||||
const filteredModels = useMemo(() => {
|
||||
let filtered = sortedModels
|
||||
// Apply search filter
|
||||
if (searchValue.length) {
|
||||
filtered = filtered?.filter(
|
||||
(e) =>
|
||||
fuzzySearch(
|
||||
searchValue.replace(/\s+/g, '').toLowerCase(),
|
||||
e.model_name.toLowerCase()
|
||||
) ||
|
||||
e.quants.some((model) =>
|
||||
fuzzySearch(
|
||||
searchValue.replace(/\s+/g, '').toLowerCase(),
|
||||
model.model_id.toLowerCase()
|
||||
)
|
||||
)
|
||||
)
|
||||
if (debouncedSearchValue.length) {
|
||||
const fuse = new Fuse(filtered, searchOptions)
|
||||
filtered = fuse.search(debouncedSearchValue).map((result) => result.item)
|
||||
}
|
||||
// Apply downloaded filter
|
||||
if (showOnlyDownloaded) {
|
||||
@ -212,11 +217,12 @@ function Hub() {
|
||||
}
|
||||
return filtered
|
||||
}, [
|
||||
searchValue,
|
||||
sortedModels,
|
||||
debouncedSearchValue,
|
||||
showOnlyDownloaded,
|
||||
llamaProvider?.models,
|
||||
huggingFaceRepo,
|
||||
searchOptions,
|
||||
llamaProvider?.models,
|
||||
])
|
||||
|
||||
// The virtualizer
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user