Adding downloading model table

Signed-off-by: James <james@jan.ai>
This commit is contained in:
James 2023-10-04 19:35:12 -07:00 committed by Louis
parent 6f50424917
commit 223a95ef3d
10 changed files with 163 additions and 56 deletions

View File

@ -0,0 +1,27 @@
import React, { Fragment } from "react";
import { modelDownloadStateAtom } from "@/_helpers/atoms/DownloadState.atom";
import { useAtomValue } from "jotai";
import ModelDownloadingTable from "../ModelDownloadingTable";
import { DownloadState } from "@/_models/DownloadState";
const DownloadingModelTable: React.FC = () => {
const modelDownloadState = useAtomValue(modelDownloadStateAtom);
const isAnyModelDownloading = Object.values(modelDownloadState).length > 0;
if (!isAnyModelDownloading) return null;
const downloadStates: DownloadState[] = [];
for (const [, value] of Object.entries(modelDownloadState)) {
downloadStates.push(value);
}
return (
<Fragment>
<h3 className="text-xl leading-[25px] mt-[50px] mb-4">Downloading Models</h3>
<ModelDownloadingTable downloadStates={downloadStates} />
</Fragment>
);
};
export default DownloadingModelTable;

View File

@ -1,12 +1,8 @@
import ExploreModelItem from "../ExploreModelItem";
import HeaderTitle from "../HeaderTitle"; import HeaderTitle from "../HeaderTitle";
import SearchBar, { SearchType } from "../SearchBar"; import SearchBar, { SearchType } from "../SearchBar";
import SimpleCheckbox from "../SimpleCheckbox"; import SimpleCheckbox from "../SimpleCheckbox";
import SimpleTag, { TagType } from "../SimpleTag"; import SimpleTag, { TagType } from "../SimpleTag";
import useGetHuggingFaceModel from "@/_hooks/useGetHuggingFaceModel"; import ExploreModelList from "../ExploreModelList";
import { useAtomValue } from "jotai";
import { modelSearchAtom } from "@/_helpers/JotaiWrapper";
import { useEffect } from "react";
const tags = [ const tags = [
"Roleplay", "Roleplay",
@ -19,15 +15,7 @@ const tags = [
]; ];
const checkboxs = ["GGUF", "TensorRT", "Meow", "JigglyPuff"]; const checkboxs = ["GGUF", "TensorRT", "Meow", "JigglyPuff"];
const ExploreModelContainer: React.FC = () => { const ExploreModelContainer: React.FC = () => (
const modelSearch = useAtomValue(modelSearchAtom);
const { modelList, getHuggingFaceModel } = useGetHuggingFaceModel();
useEffect(() => {
getHuggingFaceModel(modelSearch);
}, [modelSearch]);
return (
<div className="flex flex-col flex-1 px-16 pt-14 overflow-hidden"> <div className="flex flex-col flex-1 px-16 pt-14 overflow-hidden">
<HeaderTitle title="Explore Models" /> <HeaderTitle title="Explore Models" />
<SearchBar <SearchBar
@ -50,14 +38,9 @@ const ExploreModelContainer: React.FC = () => {
))} ))}
</fieldset> </fieldset>
</div> </div>
<div className="flex-1 pb-4 gap-y-4 overflow-y-auto scroll"> <ExploreModelList />
{modelList.map((item) => (
<ExploreModelItem key={item.id} model={item} />
))}
</div>
</div> </div>
</div> </div>
); );
};
export default ExploreModelContainer; export default ExploreModelContainer;

View File

@ -2,12 +2,9 @@
import ExploreModelItemHeader from "../ExploreModelItemHeader"; import ExploreModelItemHeader from "../ExploreModelItemHeader";
import ModelVersionList from "../ModelVersionList"; import ModelVersionList from "../ModelVersionList";
import { Fragment, useMemo, useState } from "react"; import { Fragment, useState } from "react";
import SimpleTag, { TagType } from "../SimpleTag"; import SimpleTag, { TagType } from "../SimpleTag";
import { displayDate } from "@/_utils/datetime"; 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"; import { Product } from "@/_models/Product";
type Props = { type Props = {
@ -15,21 +12,14 @@ type Props = {
}; };
const ExploreModelItem: React.FC<Props> = ({ model }) => { const ExploreModelItem: React.FC<Props> = ({ model }) => {
const downloadAtom = useMemo(
() => atom((get) => get(modelDownloadStateAtom)[model.name ?? ""]),
[model.name ?? ""]
);
const downloadState = useAtomValue(downloadAtom);
const { downloadModel } = useDownloadModel();
const [show, setShow] = useState(false); const [show, setShow] = useState(false);
return ( return (
<div className="flex flex-col border border-gray-200 rounded-[5px]"> <div className="flex flex-col border border-gray-200 rounded-md mb-4">
<ExploreModelItemHeader <ExploreModelItemHeader
name={model.name} name={model.name}
status={TagType.Recommended} status={TagType.Recommended}
versions={model.availableVersions} versions={model.availableVersions}
downloadState={downloadState}
/> />
<div className="flex flex-col px-[26px] py-[22px]"> <div className="flex flex-col px-[26px] py-[22px]">
<div className="flex justify-between"> <div className="flex justify-between">
@ -88,7 +78,12 @@ const ExploreModelItem: React.FC<Props> = ({ model }) => {
</div> </div>
{model.availableVersions.length > 0 && ( {model.availableVersions.length > 0 && (
<Fragment> <Fragment>
{show && <ModelVersionList model={model} versions={model.availableVersions} />} {show && (
<ModelVersionList
model={model}
versions={model.availableVersions}
/>
)}
<button <button
onClick={() => setShow(!show)} onClick={() => setShow(!show)}
className="bg-[#FBFBFB] text-gray-500 text-sm text-left py-2 px-4 border-t border-gray-200" className="bg-[#FBFBFB] text-gray-500 text-sm text-left py-2 px-4 border-t border-gray-200"

View File

@ -0,0 +1,24 @@
import React, { useEffect } from "react";
import ExploreModelItem from "../ExploreModelItem";
import { modelSearchAtom } from "@/_helpers/JotaiWrapper";
import useGetHuggingFaceModel from "@/_hooks/useGetHuggingFaceModel";
import { useAtomValue } from "jotai";
const ExploreModelList: React.FC = () => {
const modelSearch = useAtomValue(modelSearchAtom);
const { modelList, getHuggingFaceModel } = useGetHuggingFaceModel();
useEffect(() => {
getHuggingFaceModel(modelSearch);
}, [modelSearch]);
return (
<div className="flex-1 overflow-y-auto scroll">
{modelList.map((item) => (
<ExploreModelItem key={item.id} model={item} />
))}
</div>
);
};
export default ExploreModelList;

View File

@ -0,0 +1,37 @@
import React from "react";
import { DownloadState } from "@/_models/DownloadState";
import {
formatDownloadPercentage,
formatDownloadSpeed,
toGigabytes,
} from "@/_utils/converter";
type Props = {
downloadState: DownloadState;
};
const ModelDownloadingRow: React.FC<Props> = ({ downloadState }) => (
<tr
className="border-b border-gray-200 last:border-b-0 last:rounded-lg"
key={downloadState.fileName}
>
<td className="flex flex-col whitespace-nowrap px-6 py-4 text-sm font-medium text-gray-900">
{downloadState.fileName}
<span className="text-gray-500 font-normal">model.version</span>
</td>
<td className="whitespace-nowrap px-6 py-4 text-sm text-gray-500">
{toGigabytes(downloadState.size.transferred)}
</td>
<td className="whitespace-nowrap px-6 py-4 text-sm text-gray-500">
{toGigabytes(downloadState.size.total)}
</td>
<td className="whitespace-nowrap px-6 py-4 text-sm text-gray-500">
{formatDownloadPercentage(downloadState.percent)}
</td>
<td className="whitespace-nowrap px-6 py-4 text-sm text-gray-500">
{formatDownloadSpeed(downloadState.speed)}
</td>
</tr>
);
export default ModelDownloadingRow;

View File

@ -0,0 +1,34 @@
import React from "react";
import ModelTableHeader from "../ModelTableHeader";
import { DownloadState } from "@/_models/DownloadState";
import ModelDownloadingRow from "../ModelDownloadingRow";
type Props = {
downloadStates: DownloadState[];
};
const tableHeaders = ["MODEL", "TRANSFERRED", "SIZE", "PERCENTAGE", "SPEED"];
const ModelDownloadingTable: React.FC<Props> = ({ downloadStates }) => (
<div className="flow-root border rounded-lg border-gray-200 min-w-full align-middle shadow-lg">
<table className="min-w-full">
<thead className="bg-gray-50 border-b border-gray-200">
<tr className="rounded-t-lg">
{tableHeaders.map((item) => (
<ModelTableHeader key={item} title={item} />
))}
<th scope="col" className="relative px-6 py-3 w-fit">
<span className="sr-only">Edit</span>
</th>
</tr>
</thead>
<tbody>
{downloadStates.map((state) => (
<ModelDownloadingRow key={state.fileName} downloadState={state} />
))}
</tbody>
</table>
</div>
);
export default React.memo(ModelDownloadingTable);

View File

@ -10,7 +10,7 @@ type Props = {
const tableHeaders = ["MODEL", "FORMAT", "SIZE", "STATUS", "ACTIONS"]; const tableHeaders = ["MODEL", "FORMAT", "SIZE", "STATUS", "ACTIONS"];
const ModelTable: React.FC<Props> = ({ models }) => ( const ModelTable: React.FC<Props> = ({ models }) => (
<div className="flow-root inline-block border rounded-lg border-gray-200 min-w-full align-middle shadow-lg"> <div className="flow-root border rounded-lg border-gray-200 min-w-full align-middle shadow-lg">
<table className="min-w-full"> <table className="min-w-full">
<thead className="bg-gray-50 border-b border-gray-200"> <thead className="bg-gray-50 border-b border-gray-200">
<tr className="rounded-t-lg"> <tr className="rounded-t-lg">

View File

@ -1,11 +1,13 @@
import HeaderTitle from "../HeaderTitle"; import HeaderTitle from "../HeaderTitle";
import DownloadedModelTable from "../DownloadedModelTable"; import DownloadedModelTable from "../DownloadedModelTable";
import ActiveModelTable from "../ActiveModelTable"; import ActiveModelTable from "../ActiveModelTable";
import DownloadingModelTable from "../DownloadingModelTable";
const MyModelContainer: React.FC = () => ( const MyModelContainer: React.FC = () => (
<div className="flex flex-col w-full h-full pl-[63px] pr-[89px] pt-[60px]"> <div className="flex flex-col w-full h-full pl-[63px] pr-[89px] pt-[60px]">
<HeaderTitle title="My Models" /> <HeaderTitle title="My Models" />
<ActiveModelTable /> <ActiveModelTable />
<DownloadingModelTable />
<DownloadedModelTable /> <DownloadedModelTable />
</div> </div>
); );

View File

@ -14,7 +14,7 @@ export default function useGetHuggingFaceModel() {
const searchParams: SearchModelParamHf = { const searchParams: SearchModelParamHf = {
search: { owner }, search: { owner },
limit: 5, limit: 10,
}; };
const result = await searchHfModels(searchParams); const result = await searchHfModels(searchParams);
console.debug("result", JSON.stringify(result)); console.debug("result", JSON.stringify(result));

View File

@ -13,3 +13,8 @@ export const toGigabytes = (input: number) => {
export const formatDownloadPercentage = (input: number) => { export const formatDownloadPercentage = (input: number) => {
return (input * 100).toFixed(2) + "%"; return (input * 100).toFixed(2) + "%";
}; };
export const formatDownloadSpeed = (input: number | undefined) => {
if (!input) return "0B/s";
return toGigabytes(input) + "/s";
};