Merge branch 'main' into docs/install
This commit is contained in:
commit
f2b2247665
@ -7,7 +7,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ChatCompletionMessage,
|
|
||||||
ChatCompletionRole,
|
ChatCompletionRole,
|
||||||
EventName,
|
EventName,
|
||||||
MessageRequest,
|
MessageRequest,
|
||||||
|
|||||||
@ -109,10 +109,10 @@ async function validateModelStatus(): Promise<InitModelResponse> {
|
|||||||
return { error: undefined };
|
return { error: undefined };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { error: "Model is not loaded successfully" };
|
return { error: "Model loading failed" };
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
return { error: `Model is not loaded successfully. ${err.message}` };
|
return { error: `Model loading failed. ${err.message}` };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
21
plugins/model-plugin/src/@types/schema.ts
Normal file
21
plugins/model-plugin/src/@types/schema.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
interface Version {
|
||||||
|
name: string
|
||||||
|
quantMethod: string
|
||||||
|
bits: number
|
||||||
|
size: number
|
||||||
|
maxRamRequired: number
|
||||||
|
usecase: string
|
||||||
|
downloadLink: string
|
||||||
|
}
|
||||||
|
interface ModelSchema {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
shortDescription: string
|
||||||
|
avatarUrl: string
|
||||||
|
longDescription: string
|
||||||
|
author: string
|
||||||
|
version: string
|
||||||
|
modelUrl: string
|
||||||
|
tags: string[]
|
||||||
|
versions: Version[]
|
||||||
|
}
|
||||||
@ -1,8 +1,9 @@
|
|||||||
export const parseToModel = (model) => {
|
import { ModelCatalog } from '@janhq/core'
|
||||||
|
|
||||||
|
export function parseToModel(schema: ModelSchema): ModelCatalog {
|
||||||
const modelVersions = []
|
const modelVersions = []
|
||||||
model.versions.forEach((v) => {
|
schema.versions.forEach((v) => {
|
||||||
const version = {
|
const version = {
|
||||||
id: `${model.author}-${v.name}`,
|
|
||||||
name: v.name,
|
name: v.name,
|
||||||
quantMethod: v.quantMethod,
|
quantMethod: v.quantMethod,
|
||||||
bits: v.bits,
|
bits: v.bits,
|
||||||
@ -10,28 +11,22 @@ export const parseToModel = (model) => {
|
|||||||
maxRamRequired: v.maxRamRequired,
|
maxRamRequired: v.maxRamRequired,
|
||||||
usecase: v.usecase,
|
usecase: v.usecase,
|
||||||
downloadLink: v.downloadLink,
|
downloadLink: v.downloadLink,
|
||||||
productId: model.id,
|
|
||||||
}
|
}
|
||||||
modelVersions.push(version)
|
modelVersions.push(version)
|
||||||
})
|
})
|
||||||
|
|
||||||
const product = {
|
const model: ModelCatalog = {
|
||||||
id: model.id,
|
id: schema.id,
|
||||||
name: model.name,
|
name: schema.name,
|
||||||
shortDescription: model.shortDescription,
|
shortDescription: schema.shortDescription,
|
||||||
avatarUrl: model.avatarUrl,
|
avatarUrl: schema.avatarUrl,
|
||||||
author: model.author,
|
author: schema.author,
|
||||||
version: model.version,
|
version: schema.version,
|
||||||
modelUrl: model.modelUrl,
|
modelUrl: schema.modelUrl,
|
||||||
nsfw: model.nsfw,
|
tags: schema.tags,
|
||||||
tags: model.tags,
|
longDescription: schema.longDescription,
|
||||||
greeting: model.defaultGreeting,
|
|
||||||
type: model.type,
|
|
||||||
createdAt: model.createdAt,
|
|
||||||
longDescription: model.longDescription,
|
|
||||||
status: 'Downloadable',
|
|
||||||
releaseDate: 0,
|
releaseDate: 0,
|
||||||
availableVersions: modelVersions,
|
availableVersions: modelVersions,
|
||||||
}
|
}
|
||||||
return product
|
return model
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,9 @@ const TopBar = () => {
|
|||||||
<div className="fixed left-0 top-0 z-50 flex h-12 w-full border-b border-border bg-background/50">
|
<div className="fixed left-0 top-0 z-50 flex h-12 w-full border-b border-border bg-background/50">
|
||||||
<div className="relative left-16 flex w-[calc(100%-64px)] items-center justify-between space-x-4 pl-6 pr-2">
|
<div className="relative left-16 flex w-[calc(100%-64px)] items-center justify-between space-x-4 pl-6 pr-2">
|
||||||
<div>
|
<div>
|
||||||
<span className="font-medium">{viewStateName}</span>
|
<span className="font-medium">
|
||||||
|
{viewStateName.replace(/([A-Z])/g, ' $1').trim()}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<CommandSearch />
|
<CommandSearch />
|
||||||
{/* Command without trigger interface */}
|
{/* Command without trigger interface */}
|
||||||
|
|||||||
@ -1,12 +0,0 @@
|
|||||||
import { atom } from 'jotai'
|
|
||||||
|
|
||||||
export const showConfirmDeleteConversationModalAtom = atom(false)
|
|
||||||
export const showConfirmSignOutModalAtom = atom(false)
|
|
||||||
export const showConfirmDeleteModalAtom = atom(false)
|
|
||||||
export const showingAdvancedPromptAtom = atom<boolean>(false)
|
|
||||||
export const showingProductDetailAtom = atom<boolean>(false)
|
|
||||||
export const showingMobilePaneAtom = atom<boolean>(false)
|
|
||||||
export const showingBotListModalAtom = atom<boolean>(false)
|
|
||||||
export const showingCancelDownloadModalAtom = atom<boolean>(false)
|
|
||||||
|
|
||||||
export const showingModalNoActiveModel = atom<boolean>(false)
|
|
||||||
@ -33,7 +33,10 @@ export function useActiveModel() {
|
|||||||
const model = downloadedModels.find((e) => e.id === modelId)
|
const model = downloadedModels.find((e) => e.id === modelId)
|
||||||
|
|
||||||
if (!model) {
|
if (!model) {
|
||||||
alert(`Model ${modelId} not found! Please re-download the model first.`)
|
toaster({
|
||||||
|
title: `Model ${modelId} not found!`,
|
||||||
|
description: `Please download the model first.`,
|
||||||
|
})
|
||||||
setStateModel(() => ({
|
setStateModel(() => ({
|
||||||
state: 'start',
|
state: 'start',
|
||||||
loading: false,
|
loading: false,
|
||||||
|
|||||||
@ -20,11 +20,10 @@ export const useCreateConversation = () => {
|
|||||||
const addNewConvoState = useSetAtom(addNewConversationStateAtom)
|
const addNewConvoState = useSetAtom(addNewConversationStateAtom)
|
||||||
|
|
||||||
const requestCreateConvo = async (model: Model) => {
|
const requestCreateConvo = async (model: Model) => {
|
||||||
const summary = model.name
|
|
||||||
const mappedConvo: Thread = {
|
const mappedConvo: Thread = {
|
||||||
id: generateConversationId(),
|
id: generateConversationId(),
|
||||||
modelId: model.id,
|
modelId: model.id,
|
||||||
summary,
|
summary: model.name,
|
||||||
createdAt: new Date().toISOString(),
|
createdAt: new Date().toISOString(),
|
||||||
updatedAt: new Date().toISOString(),
|
updatedAt: new Date().toISOString(),
|
||||||
messages: [],
|
messages: [],
|
||||||
@ -35,7 +34,7 @@ export const useCreateConversation = () => {
|
|||||||
waitingForResponse: false,
|
waitingForResponse: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
pluginManager
|
await pluginManager
|
||||||
.get<ConversationalPlugin>(PluginType.Conversational)
|
.get<ConversationalPlugin>(PluginType.Conversational)
|
||||||
?.saveConversation(mappedConvo)
|
?.saveConversation(mappedConvo)
|
||||||
setUserConversations([mappedConvo, ...userConversations])
|
setUserConversations([mappedConvo, ...userConversations])
|
||||||
|
|||||||
@ -16,10 +16,6 @@ import {
|
|||||||
getActiveConvoIdAtom,
|
getActiveConvoIdAtom,
|
||||||
setActiveConvoIdAtom,
|
setActiveConvoIdAtom,
|
||||||
} from '@/helpers/atoms/Conversation.atom'
|
} from '@/helpers/atoms/Conversation.atom'
|
||||||
import {
|
|
||||||
showingProductDetailAtom,
|
|
||||||
showingAdvancedPromptAtom,
|
|
||||||
} from '@/helpers/atoms/Modal.atom'
|
|
||||||
|
|
||||||
export default function useDeleteConversation() {
|
export default function useDeleteConversation() {
|
||||||
const { activeModel } = useActiveModel()
|
const { activeModel } = useActiveModel()
|
||||||
@ -27,8 +23,6 @@ export default function useDeleteConversation() {
|
|||||||
userConversationsAtom
|
userConversationsAtom
|
||||||
)
|
)
|
||||||
const setCurrentPrompt = useSetAtom(currentPromptAtom)
|
const setCurrentPrompt = useSetAtom(currentPromptAtom)
|
||||||
const setShowingProductDetail = useSetAtom(showingProductDetailAtom)
|
|
||||||
const setShowingAdvancedPrompt = useSetAtom(showingAdvancedPromptAtom)
|
|
||||||
const activeConvoId = useAtomValue(getActiveConvoIdAtom)
|
const activeConvoId = useAtomValue(getActiveConvoIdAtom)
|
||||||
|
|
||||||
const setActiveConvoId = useSetAtom(setActiveConvoIdAtom)
|
const setActiveConvoId = useSetAtom(setActiveConvoIdAtom)
|
||||||
@ -45,18 +39,16 @@ export default function useDeleteConversation() {
|
|||||||
)
|
)
|
||||||
setUserConversations(currentConversations)
|
setUserConversations(currentConversations)
|
||||||
deleteMessages(activeConvoId)
|
deleteMessages(activeConvoId)
|
||||||
|
setCurrentPrompt('')
|
||||||
toaster({
|
toaster({
|
||||||
title: 'Succes delete a chat',
|
title: 'Chat successfully deleted.',
|
||||||
description: `Delete chat with ${activeModel?.name} has been completed`,
|
description: `Chat with ${activeModel?.name} has been successfully deleted.`,
|
||||||
})
|
})
|
||||||
if (currentConversations.length > 0) {
|
if (currentConversations.length > 0) {
|
||||||
setActiveConvoId(currentConversations[0].id)
|
setActiveConvoId(currentConversations[0].id)
|
||||||
} else {
|
} else {
|
||||||
setActiveConvoId(undefined)
|
setActiveConvoId(undefined)
|
||||||
}
|
}
|
||||||
setCurrentPrompt('')
|
|
||||||
setShowingProductDetail(false)
|
|
||||||
setShowingAdvancedPrompt(false)
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,8 +20,8 @@ export default function useDeleteModel() {
|
|||||||
// reload models
|
// reload models
|
||||||
setDownloadedModels(downloadedModels.filter((e) => e.id !== model.id))
|
setDownloadedModels(downloadedModels.filter((e) => e.id !== model.id))
|
||||||
toaster({
|
toaster({
|
||||||
title: 'Delete a Model',
|
title: 'Model Deletion Successful',
|
||||||
description: `Model ${model.id} has been deleted.`,
|
description: `The model ${model.id} has been successfully deleted.`,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,14 +20,13 @@ export function useGetConfiguredModels() {
|
|||||||
const [models, setModels] = useState<ModelCatalog[]>([])
|
const [models, setModels] = useState<ModelCatalog[]>([])
|
||||||
|
|
||||||
async function getConfiguredModels(): Promise<ModelCatalog[]> {
|
async function getConfiguredModels(): Promise<ModelCatalog[]> {
|
||||||
return (
|
const models = await pluginManager
|
||||||
((await pluginManager
|
|
||||||
.get<ModelPlugin>(PluginType.Model)
|
.get<ModelPlugin>(PluginType.Model)
|
||||||
?.getConfiguredModels()) as ModelCatalog[]) ?? []
|
?.getConfiguredModels()
|
||||||
)
|
return models ?? []
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchModels = async () => {
|
async function fetchModels() {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
let models = await getConfiguredModels()
|
let models = await getConfiguredModels()
|
||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development') {
|
||||||
@ -37,10 +36,8 @@ export function useGetConfiguredModels() {
|
|||||||
setModels(models)
|
setModels(models)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO allow user for filter
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchModels()
|
fetchModels()
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return { loading, models }
|
return { loading, models }
|
||||||
|
|||||||
@ -12,11 +12,10 @@ export function useGetDownloadedModels() {
|
|||||||
const [downloadedModels, setDownloadedModels] = useAtom(downloadedModelAtom)
|
const [downloadedModels, setDownloadedModels] = useAtom(downloadedModelAtom)
|
||||||
|
|
||||||
async function getDownloadedModels(): Promise<Model[]> {
|
async function getDownloadedModels(): Promise<Model[]> {
|
||||||
const models =
|
const models = await pluginManager
|
||||||
((await pluginManager
|
|
||||||
.get<ModelPlugin>(PluginType.Model)
|
.get<ModelPlugin>(PluginType.Model)
|
||||||
?.getDownloadedModels()) as Model[]) ?? []
|
?.getDownloadedModels()
|
||||||
return models
|
return models ?? []
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@ -10,15 +10,15 @@ import { currentConversationAtom } from '@/helpers/atoms/Conversation.atom'
|
|||||||
|
|
||||||
export default function useGetInputState() {
|
export default function useGetInputState() {
|
||||||
const [inputState, setInputState] = useState<InputType>('loading')
|
const [inputState, setInputState] = useState<InputType>('loading')
|
||||||
const currentConvo = useAtomValue(currentConversationAtom)
|
const currentThread = useAtomValue(currentConversationAtom)
|
||||||
const { activeModel } = useActiveModel()
|
const { activeModel } = useActiveModel()
|
||||||
const { downloadedModels } = useGetDownloadedModels()
|
const { downloadedModels } = useGetDownloadedModels()
|
||||||
|
|
||||||
const handleInputState = (
|
const handleInputState = (
|
||||||
convo: Thread | undefined,
|
thread: Thread | undefined,
|
||||||
currentModel: Model | undefined
|
currentModel: Model | undefined
|
||||||
) => {
|
) => {
|
||||||
if (convo == null) return
|
if (thread == null) return
|
||||||
if (currentModel == null) {
|
if (currentModel == null) {
|
||||||
setInputState('loading')
|
setInputState('loading')
|
||||||
return
|
return
|
||||||
@ -26,7 +26,7 @@ export default function useGetInputState() {
|
|||||||
|
|
||||||
// check if convo model id is in downloaded models
|
// check if convo model id is in downloaded models
|
||||||
const isModelAvailable = downloadedModels.some(
|
const isModelAvailable = downloadedModels.some(
|
||||||
(model) => model.id === convo.modelId
|
(model) => model.id === thread.modelId
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!isModelAvailable) {
|
if (!isModelAvailable) {
|
||||||
@ -35,7 +35,7 @@ export default function useGetInputState() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (convo.modelId !== currentModel.id) {
|
if (thread.modelId !== currentModel.id) {
|
||||||
// in case convo model and active model is different,
|
// in case convo model and active model is different,
|
||||||
// ask user to init the required model
|
// ask user to init the required model
|
||||||
setInputState('model-mismatch')
|
setInputState('model-mismatch')
|
||||||
@ -46,11 +46,11 @@ export default function useGetInputState() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
handleInputState(currentConvo, activeModel)
|
handleInputState(currentThread, activeModel)
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return { inputState, currentConvo }
|
return { inputState, currentThread }
|
||||||
}
|
}
|
||||||
|
|
||||||
type InputType = 'available' | 'loading' | 'model-mismatch' | 'model-not-found'
|
type InputType = 'available' | 'loading' | 'model-mismatch' | 'model-not-found'
|
||||||
|
|||||||
@ -34,14 +34,15 @@ export default function useGetSystemResources() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getSystemResources()
|
getSystemResources()
|
||||||
|
|
||||||
// Fetch interval - every 3s
|
// Fetch interval - every 5s
|
||||||
|
// TODO: Will we really need this?
|
||||||
|
// There is a possibility that this will be removed and replaced by the process event hook?
|
||||||
const intervalId = setInterval(() => {
|
const intervalId = setInterval(() => {
|
||||||
getSystemResources()
|
getSystemResources()
|
||||||
}, 5000)
|
}, 5000)
|
||||||
|
|
||||||
// clean up
|
// clean up interval
|
||||||
return () => clearInterval(intervalId)
|
return () => clearInterval(intervalId)
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -16,6 +16,8 @@ import { ulid } from 'ulid'
|
|||||||
|
|
||||||
import { currentPromptAtom } from '@/containers/Providers/Jotai'
|
import { currentPromptAtom } from '@/containers/Providers/Jotai'
|
||||||
|
|
||||||
|
import { useActiveModel } from './useActiveModel'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
addNewMessageAtom,
|
addNewMessageAtom,
|
||||||
getCurrentChatMessagesAtom,
|
getCurrentChatMessagesAtom,
|
||||||
@ -34,24 +36,21 @@ export default function useSendChatMessage() {
|
|||||||
const updateConvWaiting = useSetAtom(updateConversationWaitingForResponseAtom)
|
const updateConvWaiting = useSetAtom(updateConversationWaitingForResponseAtom)
|
||||||
const [currentPrompt, setCurrentPrompt] = useAtom(currentPromptAtom)
|
const [currentPrompt, setCurrentPrompt] = useAtom(currentPromptAtom)
|
||||||
const currentMessages = useAtomValue(getCurrentChatMessagesAtom)
|
const currentMessages = useAtomValue(getCurrentChatMessagesAtom)
|
||||||
|
const { activeModel } = useActiveModel()
|
||||||
let timeout: NodeJS.Timeout | undefined = undefined
|
|
||||||
|
|
||||||
function updateConvSummary(newMessage: MessageRequest) {
|
function updateConvSummary(newMessage: MessageRequest) {
|
||||||
if (timeout) {
|
|
||||||
clearTimeout(timeout)
|
|
||||||
}
|
|
||||||
timeout = setTimeout(() => {
|
|
||||||
const conv = currentConvo
|
|
||||||
if (
|
if (
|
||||||
!currentConvo?.summary ||
|
currentConvo &&
|
||||||
|
newMessage.messages &&
|
||||||
|
newMessage.messages.length > 2 &&
|
||||||
|
(!currentConvo.summary ||
|
||||||
currentConvo.summary === '' ||
|
currentConvo.summary === '' ||
|
||||||
currentConvo.summary.startsWith('Prompt:')
|
currentConvo.summary === activeModel?.name)
|
||||||
) {
|
) {
|
||||||
const summaryMsg: ChatCompletionMessage = {
|
const summaryMsg: ChatCompletionMessage = {
|
||||||
role: ChatCompletionRole.User,
|
role: ChatCompletionRole.User,
|
||||||
content:
|
content:
|
||||||
'summary this conversation in 5 words, the response should just include the summary',
|
'summary this conversation in a few words, the response should just include the summary',
|
||||||
}
|
}
|
||||||
// Request convo summary
|
// Request convo summary
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
@ -59,16 +58,17 @@ export default function useSendChatMessage() {
|
|||||||
.get<InferencePlugin>(PluginType.Inference)
|
.get<InferencePlugin>(PluginType.Inference)
|
||||||
?.inferenceRequest({
|
?.inferenceRequest({
|
||||||
...newMessage,
|
...newMessage,
|
||||||
messages: newMessage.messages?.concat([summaryMsg]),
|
messages: newMessage.messages?.slice(0, -1).concat([summaryMsg]),
|
||||||
})
|
})
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
currentConvo &&
|
||||||
|
currentConvo.id === newMessage.threadId &&
|
||||||
result?.message &&
|
result?.message &&
|
||||||
result.message.split(' ').length <= 10 &&
|
result?.message?.trim().length > 0 &&
|
||||||
conv?.id
|
result.message.split(' ').length <= 10
|
||||||
) {
|
) {
|
||||||
const updatedConv = {
|
const updatedConv = {
|
||||||
...conv,
|
...currentConvo,
|
||||||
summary: result.message,
|
summary: result.message,
|
||||||
}
|
}
|
||||||
updateConversation(updatedConv)
|
updateConversation(updatedConv)
|
||||||
@ -81,7 +81,6 @@ export default function useSendChatMessage() {
|
|||||||
}
|
}
|
||||||
}, 1000)
|
}, 1000)
|
||||||
}
|
}
|
||||||
}, 100)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const sendChatMessage = async () => {
|
const sendChatMessage = async () => {
|
||||||
@ -123,21 +122,7 @@ export default function useSendChatMessage() {
|
|||||||
}
|
}
|
||||||
addNewMessage(threadMessage)
|
addNewMessage(threadMessage)
|
||||||
|
|
||||||
// delay randomly from 50 - 100ms
|
|
||||||
// to prevent duplicate message id
|
|
||||||
const delay = Math.floor(Math.random() * 50) + 50
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, delay))
|
|
||||||
|
|
||||||
events.emit(EventName.OnNewMessageRequest, messageRequest)
|
events.emit(EventName.OnNewMessageRequest, messageRequest)
|
||||||
if (!currentConvo?.summary && currentConvo) {
|
|
||||||
const updatedConv: Thread = {
|
|
||||||
...currentConvo,
|
|
||||||
summary: `Prompt: ${prompt}`,
|
|
||||||
}
|
|
||||||
|
|
||||||
updateConversation(updatedConv)
|
|
||||||
}
|
|
||||||
|
|
||||||
updateConvSummary(messageRequest)
|
updateConvSummary(messageRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -58,10 +58,18 @@ const ExploreModelItem = forwardRef<HTMLDivElement, Props>(({ model }, ref) => {
|
|||||||
<div>
|
<div>
|
||||||
<span className="mb-1 font-semibold">Compatibility</span>
|
<span className="mb-1 font-semibold">Compatibility</span>
|
||||||
<div className="mt-1 flex gap-2">
|
<div className="mt-1 flex gap-2">
|
||||||
<Badge themes="secondary" className="line-clamp-1 max-w-[400px]">
|
<Badge
|
||||||
|
themes="secondary"
|
||||||
|
className="line-clamp-1 max-w-[400px] lg:line-clamp-none lg:max-w-none"
|
||||||
|
title={usecase}
|
||||||
|
>
|
||||||
{usecase}
|
{usecase}
|
||||||
</Badge>
|
</Badge>
|
||||||
<Badge themes="secondary" className="line-clamp-1">
|
<Badge
|
||||||
|
themes="secondary"
|
||||||
|
className="line-clamp-1 lg:line-clamp-none"
|
||||||
|
title={`${toGigabytes(maxRamRequired)} RAM required`}
|
||||||
|
>
|
||||||
{toGigabytes(maxRamRequired)} RAM required
|
{toGigabytes(maxRamRequired)} RAM required
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,8 +2,8 @@
|
|||||||
import React, { useMemo } from 'react'
|
import React, { useMemo } from 'react'
|
||||||
|
|
||||||
import { ModelCatalog, ModelVersion } from '@janhq/core/lib/types'
|
import { ModelCatalog, ModelVersion } from '@janhq/core/lib/types'
|
||||||
import { Button } from '@janhq/uikit'
|
import { Button, Badge } from '@janhq/uikit'
|
||||||
import { Badge } from '@janhq/uikit'
|
|
||||||
import { atom, useAtomValue } from 'jotai'
|
import { atom, useAtomValue } from 'jotai'
|
||||||
|
|
||||||
import ModalCancelDownload from '@/containers/ModalCancelDownload'
|
import ModalCancelDownload from '@/containers/ModalCancelDownload'
|
||||||
@ -73,16 +73,25 @@ const ModelVersionItem: React.FC<Props> = ({ model, modelVersion }) => {
|
|||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-between gap-4 border-t border-border pb-3 pl-3 pr-4 pt-3 first:border-t-0">
|
<div className="flex items-center justify-between gap-4 border-t border-border pb-3 pl-3 pr-4 pt-3 first:border-t-0">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="mb-4 line-clamp-1 flex-1">{modelVersion.name}</span>
|
<span className="line-clamp-1 flex-1" title={modelVersion.name}>
|
||||||
|
{modelVersion.name}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<div className="flex justify-end gap-2">
|
<div className="flex justify-end gap-2">
|
||||||
<Badge themes="secondary" className="line-clamp-1 max-w-[240px]">
|
<Badge
|
||||||
|
themes="secondary"
|
||||||
|
className="line-clamp-1 max-w-[240px] lg:line-clamp-none lg:max-w-none"
|
||||||
|
title={usecase}
|
||||||
|
>
|
||||||
{usecase}
|
{usecase}
|
||||||
</Badge>
|
</Badge>
|
||||||
<Badge themes="secondary" className="line-clamp-1 ">{`${toGigabytes(
|
|
||||||
maxRamRequired
|
<Badge
|
||||||
)} RAM required`}</Badge>
|
themes="secondary"
|
||||||
|
className="line-clamp-1"
|
||||||
|
title={`${toGigabytes(maxRamRequired)} RAM required`}
|
||||||
|
>{`${toGigabytes(maxRamRequired)} RAM required`}</Badge>
|
||||||
<Badge themes="secondary">{toGigabytes(modelVersion.size)}</Badge>
|
<Badge themes="secondary">{toGigabytes(modelVersion.size)}</Badge>
|
||||||
</div>
|
</div>
|
||||||
{downloadButton}
|
{downloadButton}
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable @typescript-eslint/naming-convention */
|
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
const API_BASE_PATH: string = '/api/v1'
|
const API_BASE_PATH: string = '/api/v1'
|
||||||
@ -48,7 +47,7 @@ export async function fetchApi(
|
|||||||
method: pluginFunc,
|
method: pluginFunc,
|
||||||
args: args,
|
args: args,
|
||||||
}),
|
}),
|
||||||
headers: { 'Content-Type': 'application/json', 'Authorization': '' },
|
headers: { contentType: 'application/json', Authorization: '' },
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
/* eslint-disable @typescript-eslint/ban-types */
|
/* eslint-disable @typescript-eslint/ban-types */
|
||||||
export class EventEmitter {
|
export class EventEmitter {
|
||||||
private handlers: Map<string, Function[]>
|
private handlers: Map<string, Function[]>
|
||||||
@ -28,6 +27,7 @@ export class EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
public emit(eventName: string, args: any): void {
|
public emit(eventName: string, args: any): void {
|
||||||
if (!this.handlers.has(eventName)) {
|
if (!this.handlers.has(eventName)) {
|
||||||
return
|
return
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/* eslint-disable @typescript-eslint/naming-convention */
|
/* eslint-disable @typescript-eslint/naming-convention */
|
||||||
import { ModelCatalog, ModelVersion } from '@janhq/core'
|
import { ModelCatalog } from '@janhq/core'
|
||||||
|
|
||||||
export const dummyModel: ModelCatalog = {
|
export const dummyModel: ModelCatalog = {
|
||||||
id: 'aladar/TinyLLama-v0-GGUF',
|
id: 'aladar/TinyLLama-v0-GGUF',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user