diff --git a/core/src/browser/extensions/model.ts b/core/src/browser/extensions/model.ts index e224ec5cc..9a3428988 100644 --- a/core/src/browser/extensions/model.ts +++ b/core/src/browser/extensions/model.ts @@ -12,6 +12,7 @@ export abstract class ModelExtension extends BaseExtension implements ModelInter return ExtensionTypeEnum.Model } + abstract configurePullOptions(configs: { [key: string]: any }): Promise abstract getModels(): Promise abstract pullModel(model: string, id?: string, name?: string): Promise abstract cancelModelPull(modelId: string): Promise diff --git a/extensions/model-extension/src/cortex.ts b/extensions/model-extension/src/cortex.ts index f81cda553..26316fbbc 100644 --- a/extensions/model-extension/src/cortex.ts +++ b/extensions/model-extension/src/cortex.ts @@ -18,6 +18,7 @@ interface ICortexAPI { deleteModel(model: string): Promise updateModel(model: object): Promise cancelModelPull(model: string): Promise + configs(body: { [key: string]: any }): Promise } type ModelList = { @@ -52,7 +53,7 @@ export class CortexAPI implements ICortexAPI { */ getModels(): Promise { return this.queue - .add(() => ky.get(`${API_URL}/models`).json()) + .add(() => ky.get(`${API_URL}/v1/models`).json()) .then((e) => typeof e === 'object' ? e.data.map((e) => this.transformModel(e)) : [] ) @@ -104,7 +105,7 @@ export class CortexAPI implements ICortexAPI { */ deleteModel(model: string): Promise { return this.queue.add(() => - ky.delete(`${API_URL}/models/${model}`).json().then() + ky.delete(`${API_URL}/v1/models/${model}`).json().then() ) } @@ -130,7 +131,7 @@ export class CortexAPI implements ICortexAPI { cancelModelPull(model: string): Promise { return this.queue.add(() => ky - .delete(`${API_URL}/models/pull`, { json: { taskId: model } }) + .delete(`${API_URL}/v1/models/pull`, { json: { taskId: model } }) .json() .then() ) @@ -142,7 +143,7 @@ export class CortexAPI implements ICortexAPI { */ async getModelStatus(model: string): Promise { return this.queue - .add(() => ky.get(`${API_URL}/models/status/${model}`)) + .add(() => ky.get(`${API_URL}/v1/models/status/${model}`)) .then((e) => true) .catch(() => false) } @@ -163,6 +164,16 @@ export class CortexAPI implements ICortexAPI { .then(() => {}) } + /** + * Configure model pull options + * @param body + */ + configs(body: { [key: string]: any }): Promise { + return this.queue.add(() => + ky.patch(`${API_URL}/v1/configs`, { json: body }).then(() => {}) + ) + } + /** * TRansform model to the expected format (e.g. parameters, settings, metadata) * @param model diff --git a/extensions/model-extension/src/index.ts b/extensions/model-extension/src/index.ts index 38c57e916..f1ce069f6 100644 --- a/extensions/model-extension/src/index.ts +++ b/extensions/model-extension/src/index.ts @@ -227,6 +227,13 @@ export default class JanModelExtension extends ModelExtension { return this.cortexAPI.getModelStatus(model) } + /** + * Configure pull options such as proxy, headers, etc. + */ + async configurePullOptions(options: { [key: string]: any }): Promise { + return this.cortexAPI.configs(options).catch((e) => console.debug(e)) + } + /** * Handle download state from main app */ diff --git a/extensions/monitoring-extension/src/node/index.ts b/extensions/monitoring-extension/src/node/index.ts index 980ee75d1..a900490f3 100644 --- a/extensions/monitoring-extension/src/node/index.ts +++ b/extensions/monitoring-extension/src/node/index.ts @@ -267,7 +267,7 @@ const updateGpuInfo = async () => } data = await updateCudaExistence(data) - console.log(data) + console.log('[MONITORING]::Cuda info: ', data) writeFileSync(GPU_INFO_FILE, JSON.stringify(data, null, 2)) log(`[APP]::${JSON.stringify(data)}`) resolve({}) @@ -344,7 +344,7 @@ const updateCudaExistence = async ( data.cuda.version = match[1] } } - console.log(data) + console.log('[MONITORING]::Finalized cuda info update: ', data) resolve() }) }) diff --git a/web/containers/Providers/DataLoader.tsx b/web/containers/Providers/DataLoader.tsx index d3d747d02..245c254ac 100644 --- a/web/containers/Providers/DataLoader.tsx +++ b/web/containers/Providers/DataLoader.tsx @@ -29,7 +29,7 @@ const DataLoader: React.FC = ({ children }) => { const setQuickAskEnabled = useSetAtom(quickAskEnabledAtom) const setJanDefaultDataFolder = useSetAtom(defaultJanDataFolderAtom) const setJanSettingScreen = useSetAtom(janSettingScreenAtom) - const { loadDataModel } = useModels() + const { loadDataModel, configurePullOptions } = useModels() useThreads() useAssistants() @@ -39,6 +39,8 @@ const DataLoader: React.FC = ({ children }) => { useEffect(() => { // Load data once loadDataModel() + // Configure pull options once + configurePullOptions() // eslint-disable-next-line react-hooks/exhaustive-deps }, []) diff --git a/web/hooks/useModels.ts b/web/hooks/useModels.ts index 88ec3afb5..75c86035a 100644 --- a/web/hooks/useModels.ts +++ b/web/hooks/useModels.ts @@ -9,13 +9,18 @@ import { ModelManager, } from '@janhq/core' -import { useSetAtom, useAtom } from 'jotai' +import { useSetAtom, useAtom, useAtomValue } from 'jotai' import { useDebouncedCallback } from 'use-debounce' import { isLocalEngine } from '@/utils/modelEngine' import { extensionManager } from '@/extension' +import { + ignoreSslAtom, + proxyAtom, + proxyEnabledAtom, +} from '@/helpers/atoms/AppConfig.atom' import { configuredModelsAtom, downloadedModelsAtom, @@ -29,6 +34,9 @@ import { const useModels = () => { const [downloadedModels, setDownloadedModels] = useAtom(downloadedModelsAtom) const setExtensionModels = useSetAtom(configuredModelsAtom) + const proxyEnabled = useAtomValue(proxyEnabledAtom) + const proxyUrl = useAtomValue(proxyAtom) + const proxyIgnoreSSL = useAtomValue(ignoreSslAtom) const getData = useCallback(() => { const getDownloadedModels = async () => { @@ -107,8 +115,25 @@ const useModels = () => { } }, [reloadData, updateStates]) + const configurePullOptions = useCallback(() => { + extensionManager + .get(ExtensionTypeEnum.Model) + ?.configurePullOptions( + proxyEnabled + ? { + proxy_url: proxyUrl, + verify_peer_ssl: !proxyIgnoreSSL, + } + : { + proxy_url: '', + verify_peer_ssl: false, + } + ) + }, [proxyEnabled, proxyUrl, proxyIgnoreSSL]) + return { loadDataModel: getData, + configurePullOptions, } } diff --git a/web/screens/Settings/Advanced/index.tsx b/web/screens/Settings/Advanced/index.tsx index 150f70398..a5956fc33 100644 --- a/web/screens/Settings/Advanced/index.tsx +++ b/web/screens/Settings/Advanced/index.tsx @@ -20,9 +20,12 @@ import { AlertTriangleIcon, AlertCircleIcon } from 'lucide-react' import { twMerge } from 'tailwind-merge' +import { useDebouncedCallback } from 'use-debounce' + import { snackbar, toaster } from '@/containers/Toast' import { useActiveModel } from '@/hooks/useActiveModel' +import useModels from '@/hooks/useModels' import { useSettings } from '@/hooks/useSettings' import DataFolder from './DataFolder' @@ -65,6 +68,7 @@ const Advanced = () => { const [dropdownOptions, setDropdownOptions] = useState( null ) + const { configurePullOptions } = useModels() const [toggle, setToggle] = useState(null) @@ -78,6 +82,15 @@ const Advanced = () => { return y['name'] }) + /** + * There could be a case where the state update is not synced + * so that retrieving state value from other hooks would not be accurate + * there is also a case where state update persist everytime user type in the input + */ + const updatePullOptions = useDebouncedCallback( + () => configurePullOptions(), + 300 + ) /** * Handle proxy change */ @@ -90,8 +103,9 @@ const Advanced = () => { } else { setProxy('') } + updatePullOptions() }, - [setPartialProxy, setProxy] + [setPartialProxy, setProxy, updatePullOptions] ) /** @@ -452,7 +466,10 @@ const Advanced = () => { setProxyEnabled(!proxyEnabled)} + onChange={() => { + setProxyEnabled(!proxyEnabled) + updatePullOptions() + }} />
{ setIgnoreSSL(e.target.checked)} + onChange={(e) => { + setIgnoreSSL(e.target.checked) + updatePullOptions() + }} />