Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions core/src/browser/extensions/engines/AIEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,4 +270,10 @@ export abstract class AIEngine extends BaseExtension {
* Optional method to get the underlying chat client
*/
getChatClient?(sessionId: string): any

/**
* Check if a tool is supported by the model
* @param modelId
*/
abstract isToolSupported(modelId: string): Promise<boolean>
}
1 change: 1 addition & 0 deletions core/src/types/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export enum AppEvent {
onAppUpdateDownloadUpdate = 'onAppUpdateDownloadUpdate',
onAppUpdateDownloadError = 'onAppUpdateDownloadError',
onAppUpdateDownloadSuccess = 'onAppUpdateDownloadSuccess',
onModelImported = 'onModelImported',

onUserSubmitQuickAsk = 'onUserSubmitQuickAsk',
onSelectedText = 'onSelectedText',
Expand Down
80 changes: 58 additions & 22 deletions extensions/llamacpp-extension/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
ImportOptions,
chatCompletionRequest,
events,
AppEvent,
} from '@janhq/core'

import { error, info, warn } from '@tauri-apps/plugin-log'
Expand All @@ -32,6 +33,7 @@ import {
import { invoke } from '@tauri-apps/api/core'
import { getProxyConfig } from './util'
import { basename } from '@tauri-apps/api/path'
import { readGgufMetadata } from '@janhq/tauri-plugin-llamacpp-api'

type LlamacppConfig = {
version_backend: string
Expand Down Expand Up @@ -1085,6 +1087,12 @@ export default class llamacpp_extension extends AIEngine {
data: modelConfig,
savePath: configPath,
})
events.emit(AppEvent.onModelImported, {
modelId,
modelPath,
mmprojPath,
size_bytes,
})
}

override async abortImport(modelId: string): Promise<void> {
Expand Down Expand Up @@ -1172,7 +1180,7 @@ export default class llamacpp_extension extends AIEngine {
const [version, backend] = cfg.version_backend.split('/')
if (!version || !backend) {
throw new Error(
"Initial setup for the backend failed due to a network issue. Please restart the app!"
'Initial setup for the backend failed due to a network issue. Please restart the app!'
)
}

Expand Down Expand Up @@ -1279,11 +1287,14 @@ export default class llamacpp_extension extends AIEngine {

try {
// TODO: add LIBRARY_PATH
const sInfo = await invoke<SessionInfo>('plugin:llamacpp|load_llama_model', {
backendPath,
libraryPath,
args,
})
const sInfo = await invoke<SessionInfo>(
'plugin:llamacpp|load_llama_model',
{
backendPath,
libraryPath,
args,
}
)
return sInfo
} catch (error) {
logger.error('Error in load command:\n', error)
Expand All @@ -1299,9 +1310,12 @@ export default class llamacpp_extension extends AIEngine {
const pid = sInfo.pid
try {
// Pass the PID as the session_id
const result = await invoke<UnloadResult>('plugin:llamacpp|unload_llama_model', {
pid: pid,
})
const result = await invoke<UnloadResult>(
'plugin:llamacpp|unload_llama_model',
{
pid: pid,
}
)

// If successful, remove from active sessions
if (result.success) {
Expand Down Expand Up @@ -1437,9 +1451,12 @@ export default class llamacpp_extension extends AIEngine {

private async findSessionByModel(modelId: string): Promise<SessionInfo> {
try {
let sInfo = await invoke<SessionInfo>('plugin:llamacpp|find_session_by_model', {
modelId,
})
let sInfo = await invoke<SessionInfo>(
'plugin:llamacpp|find_session_by_model',
{
modelId,
}
)
return sInfo
} catch (e) {
logger.error(e)
Expand Down Expand Up @@ -1516,7 +1533,9 @@ export default class llamacpp_extension extends AIEngine {

override async getLoadedModels(): Promise<string[]> {
try {
let models: string[] = await invoke<string[]>('plugin:llamacpp|get_loaded_models')
let models: string[] = await invoke<string[]>(
'plugin:llamacpp|get_loaded_models'
)
return models
} catch (e) {
logger.error(e)
Expand Down Expand Up @@ -1599,14 +1618,31 @@ export default class llamacpp_extension extends AIEngine {
throw new Error('method not implemented yet')
}

private async loadMetadata(path: string): Promise<GgufMetadata> {
try {
const data = await invoke<GgufMetadata>('plugin:llamacpp|read_gguf_metadata', {
path: path,
})
return data
} catch (err) {
throw err
}
/**
* Check if a tool is supported by the model
* Currently read from GGUF chat_template
* @param modelId
* @returns
*/
async isToolSupported(modelId: string): Promise<boolean> {
const janDataFolderPath = await getJanDataFolderPath()
const modelConfigPath = await joinPath([
this.providerPath,
'models',
modelId,
'model.yml',
])
const modelConfig = await invoke<ModelConfig>('read_yaml', {
path: modelConfigPath,
})
// model option is required
// NOTE: model_path and mmproj_path can be either relative to Jan's data folder or absolute path
const modelPath = await joinPath([
janDataFolderPath,
modelConfig.model_path,
])
return (await readGgufMetadata(modelPath)).metadata?.[
'tokenizer.chat_template'
]?.includes('tools')
}
}
6 changes: 1 addition & 5 deletions web-app/src/containers/DownloadManegement.tsx
Original file line number Diff line number Diff line change
@@ -1,90 +1,87 @@
import {

Check warning on line 1 in web-app/src/containers/DownloadManegement.tsx

View workflow job for this annotation

GitHub Actions / coverage-check

1 line is not covered with tests
Popover,
PopoverContent,
PopoverTrigger,
} from '@/components/ui/popover'
import { Progress } from '@/components/ui/progress'
import { useDownloadStore } from '@/hooks/useDownloadStore'
import { useLeftPanel } from '@/hooks/useLeftPanel'
import { useModelProvider } from '@/hooks/useModelProvider'
import { useAppUpdater } from '@/hooks/useAppUpdater'
import { abortDownload } from '@/services/models'
import { getProviders } from '@/services/providers'
import { DownloadEvent, DownloadState, events, AppEvent } from '@janhq/core'
import { IconDownload, IconX } from '@tabler/icons-react'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { toast } from 'sonner'
import { useTranslation } from '@/i18n/react-i18next-compat'

Check warning on line 15 in web-app/src/containers/DownloadManegement.tsx

View workflow job for this annotation

GitHub Actions / coverage-check

6-15 lines are not covered with tests

export function DownloadManagement() {
const { t } = useTranslation()
const { setProviders } = useModelProvider()
const { open: isLeftPanelOpen } = useLeftPanel()
const [isPopoverOpen, setIsPopoverOpen] = useState(false)
const {
downloads,
updateProgress,
localDownloadingModels,
removeDownload,
removeLocalDownloadingModel,
} = useDownloadStore()
const { updateState } = useAppUpdater()

Check warning on line 28 in web-app/src/containers/DownloadManegement.tsx

View workflow job for this annotation

GitHub Actions / coverage-check

17-28 lines are not covered with tests

const [appUpdateState, setAppUpdateState] = useState({
isDownloading: false,
downloadProgress: 0,
downloadedBytes: 0,
totalBytes: 0,
})

Check warning on line 35 in web-app/src/containers/DownloadManegement.tsx

View workflow job for this annotation

GitHub Actions / coverage-check

30-35 lines are not covered with tests

useEffect(() => {
setAppUpdateState({
isDownloading: updateState.isDownloading,
downloadProgress: updateState.downloadProgress,
downloadedBytes: updateState.downloadedBytes,
totalBytes: updateState.totalBytes,
})
}, [updateState])

Check warning on line 44 in web-app/src/containers/DownloadManegement.tsx

View workflow job for this annotation

GitHub Actions / coverage-check

37-44 lines are not covered with tests

const onAppUpdateDownloadUpdate = useCallback(
(data: {

Check warning on line 47 in web-app/src/containers/DownloadManegement.tsx

View workflow job for this annotation

GitHub Actions / coverage-check

46-47 lines are not covered with tests
progress?: number
downloadedBytes?: number
totalBytes?: number
}) => {
setAppUpdateState((prev) => ({
...prev,
isDownloading: true,
downloadProgress: data.progress || 0,
downloadedBytes: data.downloadedBytes || 0,
totalBytes: data.totalBytes || 0,
}))
},
[]
)

Check warning on line 61 in web-app/src/containers/DownloadManegement.tsx

View workflow job for this annotation

GitHub Actions / coverage-check

51-61 lines are not covered with tests

const onAppUpdateDownloadSuccess = useCallback(() => {
setAppUpdateState((prev) => ({
...prev,
isDownloading: false,
downloadProgress: 1,
}))
toast.success(t('common:toast.appUpdateDownloaded.title'), {
description: t('common:toast.appUpdateDownloaded.description'),
})
}, [t])

Check warning on line 72 in web-app/src/containers/DownloadManegement.tsx

View workflow job for this annotation

GitHub Actions / coverage-check

63-72 lines are not covered with tests

const onAppUpdateDownloadError = useCallback(() => {
setAppUpdateState((prev) => ({
...prev,
isDownloading: false,
}))
toast.error(t('common:toast.appUpdateDownloadFailed.title'), {
description: t('common:toast.appUpdateDownloadFailed.description'),
})
}, [t])

Check warning on line 82 in web-app/src/containers/DownloadManegement.tsx

View workflow job for this annotation

GitHub Actions / coverage-check

74-82 lines are not covered with tests

const downloadProcesses = useMemo(() => {

Check warning on line 84 in web-app/src/containers/DownloadManegement.tsx

View workflow job for this annotation

GitHub Actions / coverage-check

84 line is not covered with tests
// Get downloads with progress data
const downloadsWithProgress = Object.values(downloads).map((download) => ({
id: download.name,
Expand Down Expand Up @@ -185,15 +182,14 @@
console.debug('onFileDownloadSuccess', state)
removeDownload(state.modelId)
removeLocalDownloadingModel(state.modelId)
getProviders().then(setProviders)
toast.success(t('common:toast.downloadComplete.title'), {
id: 'download-complete',
description: t('common:toast.downloadComplete.description', {
item: state.modelId,
}),
})
},
[removeDownload, removeLocalDownloadingModel, setProviders, t]
[removeDownload, removeLocalDownloadingModel, t]
)

useEffect(() => {
Expand Down
Loading
Loading