chore: remove hard coded recommendation models and use cortexso featured tags (#4741)

* chore: remove hard coded recommendation models and use cortexso featured tags

* chore: polish model detail page

* chore: fix test
This commit is contained in:
Louis 2025-02-26 15:54:06 +07:00 committed by GitHub
parent 916b28044d
commit d7329c7719
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 937 additions and 1092 deletions

File diff suppressed because it is too large Load Diff

View File

@ -451,7 +451,7 @@ export default class JanModelExtension extends ModelExtension {
return this.queue.add(() =>
ky
.get(`${API_URL}/v1/models/hub?author=cortexso`)
.get(`${API_URL}/v1/models/hub?author=cortexso&tag=cortex.cpp`)
.json<Data<string>>()
.then((e) => {
e.data?.forEach((model) => {

View File

@ -35,14 +35,16 @@ import useDownloadModel from '@/hooks/useDownloadModel'
import { modelDownloadStateAtom } from '@/hooks/useDownloadState'
import { useGetEngines } from '@/hooks/useEngineManagement'
import { useGetModelSources } from '@/hooks/useModelSource'
import {
useGetModelSources,
useGetFeaturedSources,
} from '@/hooks/useModelSource'
import useRecommendedModel from '@/hooks/useRecommendedModel'
import useUpdateModelParameters from '@/hooks/useUpdateModelParameters'
import { formatDownloadPercentage, toGigabytes } from '@/utils/converter'
import { manualRecommendationModel } from '@/utils/model'
import { getLogoEngine, getTitleByEngine } from '@/utils/modelEngine'
import { extractModelName } from '@/utils/modelSource'
@ -93,6 +95,7 @@ const ModelDropdown = ({
const [dropdownOptions, setDropdownOptions] = useState<HTMLDivElement | null>(
null
)
const { sources: featuredModels } = useGetFeaturedSources()
const { engines } = useGetEngines()
@ -103,9 +106,6 @@ const ModelDropdown = ({
const configuredModels = useAtomValue(configuredModelsAtom)
const { stopModel } = useActiveModel()
const featuredModels = sources?.filter((x) =>
manualRecommendationModel.includes(x.id)
)
const { updateThreadMetadata } = useCreateNewThread()
const engineList = useMemo(

View File

@ -36,6 +36,22 @@ export function useGetModelSources() {
return { sources, error, mutate }
}
/**
* @returns A Promise that resolves to featured model sources.
*/
export function useGetFeaturedSources() {
const { sources, error, mutate } = useGetModelSources()
return {
sources: sources?.filter((e) => e.metadata?.tags.includes('featured')),
error,
mutate,
}
}
/**
* @returns A Promise that resolves to model source mutation.
*/
export const useModelSourcesMutation = () => {
const extension = useMemo(
() => extensionManager.get<ModelExtension>(ExtensionTypeEnum.Model),

View File

@ -23,7 +23,7 @@ import { useRefreshModelList } from '@/hooks/useEngineManagement'
import { MarkdownTextMessage } from '@/screens/Thread/ThreadCenterPanel/TextMessage/MarkdownTextMessage'
import { toGigabytes } from '@/utils/converter'
import { extractModelName } from '@/utils/modelSource'
import { extractModelName, removeYamlFrontMatter } from '@/utils/modelSource'
import { mainViewStateAtom } from '@/helpers/atoms/App.atom'
import {
@ -239,7 +239,7 @@ const ModelPage = ({ model, onGoBack }: Props) => {
{/* README */}
<div className="mt-8 flex w-full flex-col items-start justify-between sm:flex-row">
<MarkdownTextMessage
text={model.metadata?.description ?? ''}
text={removeYamlFrontMatter(model.metadata?.description ?? '')}
className="h-full w-full text-[hsla(var(--text-secondary))]"
/>
</div>

View File

@ -72,6 +72,7 @@ describe('OnDeviceStarterScreen', () => {
error: null,
mutate: jest.fn(),
})
jest.spyOn(source, 'useGetFeaturedSources').mockReturnValue([])
render(
<Provider>
<OnboardingScreen isShowStarterScreen={true} />
@ -88,6 +89,7 @@ describe('OnDeviceStarterScreen', () => {
error: null,
mutate: jest.fn(),
})
jest.spyOn(source, 'useGetFeaturedSources').mockReturnValue([])
render(
<Provider>
<OnboardingScreen isShowStarterScreen={true} />
@ -108,6 +110,7 @@ describe('OnDeviceStarterScreen', () => {
error: null,
mutate: jest.fn(),
})
jest.spyOn(source, 'useGetFeaturedSources').mockReturnValue([])
render(
<Provider>
<OnboardingScreen isShowStarterScreen={true} />
@ -126,31 +129,31 @@ describe('OnDeviceStarterScreen', () => {
id: 'cortexso/deepseek-r1',
name: 'DeepSeek R1',
metadata: {
tags: ['Featured'],
author: 'Test Author',
size: 3000000000,
tags: ['featured'],
},
models: [
{
id: 'cortexso/deepseek-r1',
name: 'DeepSeek R1',
metadata: {
tags: ['Featured'],
},
metadata: {},
},
],
},
{
id: 'cortexso/llama3.2',
name: 'Llama 3.1',
metadata: { tags: [], author: 'Test Author', size: 2000000000 },
metadata: {
author: 'Test Author',
size: 2000000000,
tags: ['featured'],
},
models: [
{
id: 'cortexso/deepseek-r1',
name: 'DeepSeek R1',
metadata: {
tags: ['Featured'],
},
metadata: {},
},
],
},
@ -161,6 +164,13 @@ describe('OnDeviceStarterScreen', () => {
error: null,
mutate: jest.fn(),
})
jest
.spyOn(source, 'useGetFeaturedSources')
.mockReturnValue({
sources: mockConfiguredModels,
error: null,
mutate: jest.fn(),
})
render(
<Provider>
@ -182,6 +192,10 @@ describe('OnDeviceStarterScreen', () => {
{ id: 'remote-model-2', name: 'Remote Model 2', engine: 'anthropic' },
]
jest
.spyOn(source, 'useGetFeaturedSources')
.mockReturnValue(mockRemoteModels)
mockAtomValue.mockImplementation((atom) => {
if (atom === jotai.atom([])) {
return mockRemoteModels

View File

@ -26,23 +26,19 @@ import { modelDownloadStateAtom } from '@/hooks/useDownloadState'
import { useGetEngines } from '@/hooks/useEngineManagement'
import { useGetModelSources } from '@/hooks/useModelSource'
import {
useGetFeaturedSources,
useGetModelSources,
} from '@/hooks/useModelSource'
import { formatDownloadPercentage, toGigabytes } from '@/utils/converter'
import { manualRecommendationModel } from '@/utils/model'
import {
getLogoEngine,
getTitleByEngine,
isLocalEngine,
} from '@/utils/modelEngine'
import { getLogoEngine, getTitleByEngine } from '@/utils/modelEngine'
import { extractModelName } from '@/utils/modelSource'
import { mainViewStateAtom } from '@/helpers/atoms/App.atom'
import {
configuredModelsAtom,
getDownloadingModelAtom,
} from '@/helpers/atoms/Model.atom'
import { getDownloadingModelAtom } from '@/helpers/atoms/Model.atom'
import {
selectedSettingAtom,
showScrollBarAtom,
@ -65,9 +61,7 @@ function OnboardingScreen({ isShowStarterScreen }: Props) {
const { sources } = useGetModelSources()
const setMainViewState = useSetAtom(mainViewStateAtom)
const featuredModels = sources?.filter((x) =>
manualRecommendationModel.includes(x.id)
)
const { sources: featuredModels } = useGetFeaturedSources()
const filteredModels = useMemo(
() =>

View File

@ -7,13 +7,3 @@
export const normalizeModelId = (downloadUrl: string): string => {
return downloadUrl.split('/').pop() ?? downloadUrl
}
/**
* Default models to recommend to users when they first open the app.
* TODO: These will be replaced when we have a proper recommendation system
* AND cortexso repositories are updated with tags.
*/
export const manualRecommendationModel = [
'cortexso/deepseek-r1',
'cortexso/llama3.2',
]

View File

@ -4,12 +4,22 @@
*/
export const extractDescription = (text?: string) => {
if (!text) return text
const normalizedText = removeYamlFrontMatter(text)
const overviewPattern = /(?:##\s*Overview\s*\n)([\s\S]*?)(?=\n\s*##|$)/
const matches = text?.match(overviewPattern)
const matches = normalizedText?.match(overviewPattern)
if (matches && matches[1]) {
return matches[1].trim()
}
return text?.slice(0, 500).trim()
return normalizedText?.slice(0, 500).trim()
}
/**
* Remove YAML (HF metadata) front matter from content
* @param content
* @returns
*/
export const removeYamlFrontMatter = (content: string): string => {
return content.replace(/^---\n([\s\S]*?)\n---\n/, '')
}
/**