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:
parent
916b28044d
commit
d7329c7719
File diff suppressed because it is too large
Load Diff
@ -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) => {
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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(
|
||||
() =>
|
||||
|
||||
@ -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',
|
||||
]
|
||||
|
||||
@ -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/, '')
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user