From 2352c316c0e8f942d8e9cd0100e9cad141790e21 Mon Sep 17 00:00:00 2001 From: jialin Date: Sat, 15 Jun 2024 20:41:26 +0800 Subject: [PATCH] fix: search file name by repo_id --- package.json | 1 + pnpm-lock.yaml | 19 +++++++ src/components/seal-form/auto-complete.tsx | 5 +- src/pages/llmodels/apis/index.ts | 14 +++--- src/pages/llmodels/components/add-modal.tsx | 56 +++++++++++++++++++-- src/utils/index.ts | 17 +++++++ 6 files changed, 98 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 2364641d..cbe4a85c 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@ant-design/icons": "^5.3.7", "@ant-design/plots": "^2.2.2", "@ant-design/pro-components": "^2.7.1", + "@huggingface/hub": "^0.15.1", "@monaco-editor/react": "^4.6.0", "@types/lodash": "^4.17.4", "@umijs/max": "^4.2.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8aaceb2e..78867550 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ dependencies: '@ant-design/pro-components': specifier: ^2.7.1 version: 2.7.1(antd@5.17.0)(rc-field-form@1.44.0)(react-dom@18.3.1)(react@18.3.1) + '@huggingface/hub': + specifier: ^0.15.1 + version: 0.15.1 '@monaco-editor/react': specifier: ^4.6.0 version: 4.6.0(monaco-editor@0.49.0)(react-dom@18.3.1)(react@18.3.1) @@ -3837,6 +3840,18 @@ packages: deprecated: the package is rather renamed to @formatjs/ecma-abstract with some changes in functionality (primarily selectUnit is removed and we don't plan to make any further changes to this package dev: false + /@huggingface/hub@0.15.1: + resolution: {integrity: sha512-uHb4aFkJDoGfLeRHfFTjkI36Z8IV6Z1c+KzhMDqUSC56opyr7Mn1Nsx7Rri/C7KDwROhQfBp/fOOqqjTzn6Cgg==, tarball: https://registry.npmjs.org/@huggingface/hub/-/hub-0.15.1.tgz} + engines: {node: '>=18'} + dependencies: + '@huggingface/tasks': 0.10.14 + hash-wasm: 4.11.0 + dev: false + + /@huggingface/tasks@0.10.14: + resolution: {integrity: sha512-8Q3aqTO+ldTTqtK4OfMz/h5DiiMBzUnZKdV0Dq2+JX+UXvqnTDVOk+bJd0QVytJYyNeZgKsj7XQHvEQGyo9cFg==, tarball: https://registry.npmjs.org/@huggingface/tasks/-/tasks-0.10.14.tgz} + dev: false + /@humanwhocodes/config-array@0.11.14: resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} @@ -9830,6 +9845,10 @@ packages: safe-buffer: 5.2.1 dev: false + /hash-wasm@4.11.0: + resolution: {integrity: sha512-HVusNXlVqHe0fzIzdQOGolnFN6mX/fqcrSAOcTBXdvzrXVHwTz11vXeKRmkR5gTuwVpvHZEIyKoePDvuAR+XwQ==, tarball: https://registry.npmjs.org/hash-wasm/-/hash-wasm-4.11.0.tgz} + dev: false + /hash.js@1.1.7: resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} dependencies: diff --git a/src/components/seal-form/auto-complete.tsx b/src/components/seal-form/auto-complete.tsx index e1b76e8a..9981e59c 100644 --- a/src/components/seal-form/auto-complete.tsx +++ b/src/components/seal-form/auto-complete.tsx @@ -13,6 +13,7 @@ const SealAutoComplete: React.FC = ( required, description, isInFormItems = true, + onSelect, ...rest } = props; const [isFocus, setIsFocus] = useState(false); @@ -48,8 +49,8 @@ const SealAutoComplete: React.FC = ( const handleOnBlur = (e: any) => { if (!props.value) { setIsFocus(false); - props.onBlur?.(e); } + props.onBlur?.(e); }; const handleSearch = (text: string) => { @@ -57,7 +58,7 @@ const SealAutoComplete: React.FC = ( }; const handleOnSelect = (value: any, option: any) => { - props.onSelect?.(value, option); + onSelect?.(value, option); }; return ( diff --git a/src/pages/llmodels/apis/index.ts b/src/pages/llmodels/apis/index.ts index 667a5db6..5675eaa7 100644 --- a/src/pages/llmodels/apis/index.ts +++ b/src/pages/llmodels/apis/index.ts @@ -1,3 +1,4 @@ +import { listFiles } from '@huggingface/hub'; import { request } from '@umijs/max'; import { FormData, @@ -115,11 +116,10 @@ export async function callHuggingfaceQuickSearch(params: any) { }); } -export async function queryHuggingfaceModelFiles(params: any) { - return request( - `https://huggingface.co/openbmb/MiniCPM-Llama3-V-2_5-gguf/tree/main`, - { - method: 'GET' - } - ); +export async function queryHuggingfaceModelFiles(params: { repo: string }) { + const result = []; + for await (const fileInfo of listFiles(params)) { + result.push(fileInfo); + } + return result; } diff --git a/src/pages/llmodels/components/add-modal.tsx b/src/pages/llmodels/components/add-modal.tsx index eb444870..ba6c158d 100644 --- a/src/pages/llmodels/components/add-modal.tsx +++ b/src/pages/llmodels/components/add-modal.tsx @@ -4,10 +4,14 @@ import SealInput from '@/components/seal-form/seal-input'; import SealSelect from '@/components/seal-form/seal-select'; import { PageAction } from '@/config'; import { PageActionType } from '@/config/types'; +import { convertFileSize } from '@/utils'; import { Form, Modal } from 'antd'; import _ from 'lodash'; import { useEffect, useState } from 'react'; -import { callHuggingfaceQuickSearch } from '../apis'; +import { + callHuggingfaceQuickSearch, + queryHuggingfaceModelFiles +} from '../apis'; import { FormData } from '../config/types'; type AddModalProps = { @@ -51,9 +55,41 @@ const AddModal: React.FC = (props) => { console.log('repo change', value); }; - const debounceSearch = _.debounce((text: string) => { - handleOnSearchRepo(text); - }, 300); + const fileNamLabel = (item: any) => { + return ( + + {item.path} + + ({convertFileSize(item.size)}) + + + ); + }; + const handleRepoSelect = async (repo: string) => { + try { + const res = await queryHuggingfaceModelFiles({ repo }); + const list = _.filter(res, (file: any) => { + return _.endsWith(file.path, '.gguf'); + }).map((item: any) => { + return { + label: fileNamLabel(item), + value: item.path, + size: item.size + }; + }); + setFileOptions(list); + } catch (error) { + setFileOptions([]); + } + }; + + const handleRepoOnBlur = (e: any) => { + const repo = form.getFieldValue('huggingface_repo_id'); + console.log('repo blur', repo); + handleRepoSelect(repo); + }; const handleOnSearchRepo = async (text: string) => { try { @@ -74,6 +110,10 @@ const AddModal: React.FC = (props) => { } }; + const debounceSearch = _.debounce((text: string) => { + handleOnSearchRepo(text); + }, 300); + const renderHuggingfaceFields = () => { return ( <> @@ -85,6 +125,7 @@ const AddModal: React.FC = (props) => { label="Repo ID" required showSearch + onBlur={handleRepoOnBlur} onChange={handleInputRepoChange} onSearch={debounceSearch} options={repoOptions} @@ -94,7 +135,12 @@ const AddModal: React.FC = (props) => { name="huggingface_filename" rules={[{ required: true }]} > - + ); diff --git a/src/utils/index.ts b/src/utils/index.ts index 91039b58..fad4d786 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -11,3 +11,20 @@ export const handleBatchRequest = async ( ) => { return Promise.all(list.map((item) => fn(item))); }; + +export const convertFileSize = (sizeInBytes: number) => { + if (!sizeInBytes) { + return '0 B'; + } + if (sizeInBytes < 1024) { + return `${sizeInBytes.toFixed(2)} B`; + } else if (sizeInBytes < 1024 * 1024) { + return `${(sizeInBytes / 1024).toFixed(2)} KB`; + } else if (sizeInBytes < 1024 * 1024 * 1024) { + return `${(sizeInBytes / (1024 * 1024)).toFixed(2)} MB`; + } else if (sizeInBytes < 1024 * 1024 * 1024 * 1024) { + return `${(sizeInBytes / (1024 * 1024 * 1024)).toFixed(2)} GB`; + } else { + return `${(sizeInBytes / (1024 * 1024 * 1024 * 1024)).toFixed(2)} TB`; + } +};