diff --git a/electron/core/plugins/model-management-plugin/index.js b/electron/core/plugins/model-management-plugin/index.js
index 0d2449931..1e5b57eac 100644
--- a/electron/core/plugins/model-management-plugin/index.js
+++ b/electron/core/plugins/model-management-plugin/index.js
@@ -38,10 +38,20 @@ const deleteModel = async (path) =>
}
});
+const searchHfModels = async (params) =>
+ new Promise(async (resolve) => {
+ if (window.electronAPI) {
+ window.electronAPI
+ .invokePluginFunc(MODULE_PATH, "searchHfModels", params)
+ .then((res) => resolve(res));
+ }
+ });
+
// Register all the above functions and objects with the relevant extension points
export function init({ register }) {
register("getDownloadedModels", "getDownloadedModels", getDownloadedModels);
register("getAvailableModels", "getAvailableModels", getAvailableModels);
register("downloadModel", "downloadModel", downloadModel);
register("deleteModel", "deleteModel", deleteModel);
+ register("searchHfModels", "searchHfModels", searchHfModels);
}
diff --git a/electron/core/plugins/model-management-plugin/module.js b/electron/core/plugins/model-management-plugin/module.js
index e17dc59e7..a944658b2 100644
--- a/electron/core/plugins/model-management-plugin/module.js
+++ b/electron/core/plugins/model-management-plugin/module.js
@@ -1,6 +1,7 @@
const path = require("path");
const { readdirSync, lstatSync } = require("fs");
const { app } = require("electron");
+const { listModels, listFiles, fileDownloadInfo } = require("@huggingface/hub");
const ALL_MODELS = [
{
@@ -87,6 +88,49 @@ function getDownloadedModels() {
return downloadedModels;
}
+const searchHfModels = async (params) => {
+ const result = [];
+
+ var index = 0;
+
+ for await (const model of listModels({
+ search: params.search,
+ credentials: params.credentials,
+ })) {
+ const files = await listFilesByName(model.name);
+ result.push({
+ ...model,
+ files,
+ });
+
+ index++;
+ if (index === params.limit) break;
+ }
+
+ return result;
+};
+
+const listFilesByName = async (modelName) => {
+ const repo = { type: "model", name: modelName };
+ const fileDownloadInfoMap = {};
+ for await (const file of listFiles({
+ repo: repo,
+ })) {
+ if (file.type === "file" && file.path.endsWith(".bin")) {
+ const downloadInfo = await fileDownloadInfo({
+ repo: repo,
+ path: file.path,
+ });
+ fileDownloadInfoMap[file.path] = {
+ ...file,
+ ...downloadInfo,
+ };
+ }
+ }
+
+ return fileDownloadInfoMap;
+};
+
function getAvailableModels() {
const downloadedModelIds = getDownloadedModels().map((model) => model.id);
return ALL_MODELS.filter((model) => {
@@ -99,4 +143,5 @@ function getAvailableModels() {
module.exports = {
getDownloadedModels,
getAvailableModels,
+ searchHfModels,
};
diff --git a/electron/core/plugins/model-management-plugin/package.json b/electron/core/plugins/model-management-plugin/package.json
index 6a1b61a32..bc2dc693e 100644
--- a/electron/core/plugins/model-management-plugin/package.json
+++ b/electron/core/plugins/model-management-plugin/package.json
@@ -24,5 +24,11 @@
"dist/*",
"package.json",
"README.md"
+ ],
+ "dependencies": {
+ "@huggingface/hub": "^0.8.5"
+ },
+ "bundledDependencies": [
+ "@huggingface/hub"
]
}
diff --git a/web/app/_components/ChatContainer/index.tsx b/web/app/_components/ChatContainer/index.tsx
index 9a002ab13..c55eeef2e 100644
--- a/web/app/_components/ChatContainer/index.tsx
+++ b/web/app/_components/ChatContainer/index.tsx
@@ -1,7 +1,6 @@
"use client";
import { useAtomValue } from "jotai";
-import { ReactNode } from "react";
import Welcome from "../WelcomeContainer";
import { Preferences } from "../Preferences";
import MyModelContainer from "../MyModelContainer";
@@ -11,17 +10,14 @@ import {
getMainViewStateAtom,
} from "@/_helpers/atoms/MainView.atom";
import EmptyChatContainer from "../EmptyChatContainer";
+import MainChat from "../MainChat";
-type Props = {
- children: ReactNode;
-};
-
-export default function ChatContainer({ children }: Props) {
+export default function ChatContainer() {
const viewState = useAtomValue(getMainViewStateAtom);
switch (viewState) {
case MainViewState.ConversationEmptyModel:
- return
+ return ;
case MainViewState.ExploreModel:
return ;
case MainViewState.Setting:
@@ -32,6 +28,6 @@ export default function ChatContainer({ children }: Props) {
case MainViewState.Welcome:
return ;
default:
- return
{children}
;
+ return ;
}
}
diff --git a/web/app/_components/ExploreModelContainer/index.tsx b/web/app/_components/ExploreModelContainer/index.tsx
index d07745ffa..88377cbf5 100644
--- a/web/app/_components/ExploreModelContainer/index.tsx
+++ b/web/app/_components/ExploreModelContainer/index.tsx
@@ -1,9 +1,12 @@
-import useGetAvailableModels from "@/_hooks/useGetAvailableModels";
import ExploreModelItem from "../ExploreModelItem";
import HeaderTitle from "../HeaderTitle";
-import SearchBar from "../SearchBar";
+import SearchBar, { SearchType } from "../SearchBar";
import SimpleCheckbox from "../SimpleCheckbox";
import SimpleTag, { TagType } from "../SimpleTag";
+import useGetHuggingFaceModel from "@/_hooks/useGetHuggingFaceModel";
+import { useAtomValue } from "jotai";
+import { modelSearchAtom } from "@/_helpers/JotaiWrapper";
+import { useEffect } from "react";
const tags = [
"Roleplay",
@@ -17,14 +20,22 @@ const tags = [
const checkboxs = ["GGUF", "TensorRT", "Meow", "JigglyPuff"];
const ExploreModelContainer: React.FC = () => {
- const { allAvailableModels } = useGetAvailableModels();
+ const modelSearch = useAtomValue(modelSearchAtom);
+ const { modelList, getHuggingFaceModel } = useGetHuggingFaceModel();
+
+ useEffect(() => {
+ getHuggingFaceModel(modelSearch);
+ }, [modelSearch]);
return (
-
+
-
-
-
+
+
+
Tags
@@ -39,13 +50,10 @@ const ExploreModelContainer: React.FC = () => {
))}
-
-
Results
-
- {allAvailableModels.map((item) => (
-
- ))}
-
+
+ {modelList.map((item) => (
+
+ ))}
diff --git a/web/app/_components/ExploreModelItem/index.tsx b/web/app/_components/ExploreModelItem/index.tsx
index 35d1de650..38fd6bd92 100644
--- a/web/app/_components/ExploreModelItem/index.tsx
+++ b/web/app/_components/ExploreModelItem/index.tsx
@@ -2,13 +2,13 @@
import ExploreModelItemHeader from "../ExploreModelItemHeader";
import ModelVersionList from "../ModelVersionList";
-import { useMemo, useState } from "react";
-import { Product } from "@/_models/Product";
+import { Fragment, useMemo, useState } from "react";
import SimpleTag, { TagType } from "../SimpleTag";
import { displayDate } from "@/_utils/datetime";
import useDownloadModel from "@/_hooks/useDownloadModel";
import { atom, useAtomValue } from "jotai";
import { modelDownloadStateAtom } from "@/_helpers/atoms/DownloadState.atom";
+import { Product } from "@/_models/Product";
type Props = {
model: Product;
@@ -16,8 +16,8 @@ type Props = {
const ExploreModelItem: React.FC
= ({ model }) => {
const downloadAtom = useMemo(
- () => atom((get) => get(modelDownloadStateAtom)[model.fileName ?? ""]),
- [model.fileName ?? ""]
+ () => atom((get) => get(modelDownloadStateAtom)[model.name ?? ""]),
+ [model.name ?? ""]
);
const downloadState = useAtomValue(downloadAtom);
const { downloadModel } = useDownloadModel();
@@ -28,9 +28,8 @@ const ExploreModelItem: React.FC = ({ model }) => {
downloadModel(model)}
/>
@@ -87,13 +86,17 @@ const ExploreModelItem: React.FC
= ({ model }) => {
Tags
- {show && }
-
+ {model.availableVersions.length > 0 && (
+
+ {show && }
+
+
+ )}
);
};
diff --git a/web/app/_components/ExploreModelItemHeader/index.tsx b/web/app/_components/ExploreModelItemHeader/index.tsx
index 83a73d97d..4bfeea035 100644
--- a/web/app/_components/ExploreModelItemHeader/index.tsx
+++ b/web/app/_components/ExploreModelItemHeader/index.tsx
@@ -3,11 +3,13 @@ import PrimaryButton from "../PrimaryButton";
import { formatDownloadPercentage, toGigabytes } from "@/_utils/converter";
import { DownloadState } from "@/_models/DownloadState";
import SecondaryButton from "../SecondaryButton";
+import { ModelVersion } from "@/_models/Product";
type Props = {
name: string;
- total: number;
status: TagType;
+ versions: ModelVersion[];
+ size?: number;
downloadState?: DownloadState;
onDownloadClick?: () => void;
};
@@ -15,30 +17,41 @@ type Props = {
const ExploreModelItemHeader: React.FC
= ({
name,
status,
- total,
+ size,
+ versions,
downloadState,
onDownloadClick,
-}) => (
-
-
- {name}
-
-
- {downloadState != null ? (
+}) => {
+ let downloadButton = (
+
onDownloadClick?.()}
+ />
+ );
+
+ if (downloadState != null) {
+ // downloading
+ downloadButton = (
{}}
/>
- ) : (
- onDownloadClick?.()}
- />
- )}
-
-);
+ );
+ } else if (versions.length === 0) {
+ downloadButton = ;
+ }
+
+ return (
+
+
+ {name}
+
+
+ {downloadButton}
+
+ );
+};
export default ExploreModelItemHeader;
diff --git a/web/app/_components/HistoryList/index.tsx b/web/app/_components/HistoryList/index.tsx
index 391b5504b..0ce2c0c4c 100644
--- a/web/app/_components/HistoryList/index.tsx
+++ b/web/app/_components/HistoryList/index.tsx
@@ -18,14 +18,14 @@ const HistoryList: React.FC = () => {
}, []);
return (
-
+
setExpand(!expand)}
/>
{conversations.length > 0 ? (
conversations
diff --git a/web/app/_components/LeftContainer/index.tsx b/web/app/_components/LeftContainer/index.tsx
index df525ce49..d99bebafb 100644
--- a/web/app/_components/LeftContainer/index.tsx
+++ b/web/app/_components/LeftContainer/index.tsx
@@ -6,7 +6,7 @@ import HistoryList from "../HistoryList";
import NewChatButton from "../NewChatButton";
const LeftContainer: React.FC = () => (
-
+
diff --git a/web/app/_components/ModelVersionItem/index.tsx b/web/app/_components/ModelVersionItem/index.tsx
index 355128bba..ab530a87a 100644
--- a/web/app/_components/ModelVersionItem/index.tsx
+++ b/web/app/_components/ModelVersionItem/index.tsx
@@ -1,25 +1,57 @@
-import React from "react";
-import { toGigabytes } from "@/_utils/converter";
+import React, { useMemo } from "react";
+import { formatDownloadPercentage, toGigabytes } from "@/_utils/converter";
import Image from "next/image";
+import { ModelVersion, Product } from "@/_models/Product";
+import useDownloadModel from "@/_hooks/useDownloadModel";
+import { modelDownloadStateAtom } from "@/_helpers/atoms/DownloadState.atom";
+import { atom, useAtomValue } from "jotai";
type Props = {
- title: string;
- totalSizeInByte: number;
+ model: Product;
+ modelVersion: ModelVersion;
};
-const ModelVersionItem: React.FC
= ({ title, totalSizeInByte }) => (
-
-
-
- {title}
-
-
-
- {toGigabytes(totalSizeInByte)}
+const ModelVersionItem: React.FC
= ({ model, modelVersion }) => {
+ const { downloadHfModel } = useDownloadModel();
+ const downloadAtom = useMemo(
+ () => atom((get) => get(modelDownloadStateAtom)[modelVersion.path ?? ""]),
+ [modelVersion.path ?? ""]
+ );
+ const downloadState = useAtomValue(downloadAtom);
+
+ const onDownloadClick = () => {
+ downloadHfModel(model, modelVersion);
+ };
+
+ let downloadButton = (
+
+ );
+
+ if (downloadState) {
+ downloadButton = (
+ {formatDownloadPercentage(downloadState.percent)}
+ );
+ }
+
+ return (
+
+
+
+ {modelVersion.path}
+
+
+
+ {toGigabytes(modelVersion.size)}
+
+ {downloadButton}
-
-
-);
+ );
+};
export default ModelVersionItem;
diff --git a/web/app/_components/ModelVersionList/index.tsx b/web/app/_components/ModelVersionList/index.tsx
index f70d38a76..09af4a623 100644
--- a/web/app/_components/ModelVersionList/index.tsx
+++ b/web/app/_components/ModelVersionList/index.tsx
@@ -1,38 +1,21 @@
import React from "react";
import ModelVersionItem from "../ModelVersionItem";
+import { ModelVersion, Product } from "@/_models/Product";
-const data = [
- {
- name: "Q4_K_M.gguf",
- total: 5600,
- },
- {
- name: "Q4_K_M.gguf",
- total: 5600,
- },
- {
- name: "Q4_K_M.gguf",
- total: 5600,
- },
-];
-
-const ModelVersionList: React.FC = () => {
- return (
-
-
- Available Versions
-
-
- {data.map((item, index) => (
-
- ))}
-
-
- );
+type Props = {
+ model: Product;
+ versions: ModelVersion[];
};
+const ModelVersionList: React.FC
= ({ model, versions }) => (
+
+
Available Versions
+
+ {versions.map((item) => (
+
+ ))}
+
+
+);
+
export default ModelVersionList;
diff --git a/web/app/_components/NewChatButton/index.tsx b/web/app/_components/NewChatButton/index.tsx
index 3fc7de8e8..a7fbcddc2 100644
--- a/web/app/_components/NewChatButton/index.tsx
+++ b/web/app/_components/NewChatButton/index.tsx
@@ -32,7 +32,7 @@ const NewChatButton: React.FC = () => {
};
return (
-
+
);
};
diff --git a/web/app/_components/PrimaryButton/index.tsx b/web/app/_components/PrimaryButton/index.tsx
index d87249b44..c87aa9717 100644
--- a/web/app/_components/PrimaryButton/index.tsx
+++ b/web/app/_components/PrimaryButton/index.tsx
@@ -14,7 +14,7 @@ const PrimaryButton: React.FC = ({