import PluginExtraFields from '@/components/plugin-extra-fields'; import { ModelFileFormData as FormData } from '@/pages/resources/config/types'; import { Input as CInput, Cascader as SealCascader, Select as SealSelect, TooltipList, useAppUtils } from '@gpustack/core-ui'; import { useIntl } from '@umijs/max'; import { Form } from 'antd'; import _ from 'lodash'; import minimatch from 'minimatch'; import React, { forwardRef, useEffect, useImperativeHandle, useMemo, useRef } from 'react'; import { localPathTipsList, modelSourceMap, sourceOptions } from '../../config'; import { useGenerateWorkersModelFileOptions } from '../../hooks'; type EmptyObject = Record; type CascaderOption = { label: string; value: string | number; parent?: boolean; disabled?: boolean; index?: number; children?: CascaderOption[]; } & Partial; interface TargetFormProps { ref?: any; source: string; workerOptions: CascaderOption<{ state: string }>[]; workersList?: Global.BaseOption< number, { state: string; labels: Record; cluster_id: number } >[]; selectedModel?: Record; fileName?: string; onOk: (values: any) => void; } const TargetForm: React.FC = forwardRef((props, ref) => { const { modelFileOptions, getModelFileList, generateWorkersModelFileOptions } = useGenerateWorkersModelFileOptions(); const { onOk, source, workerOptions, workersList, selectedModel, fileName } = props; const { getRuleMessage } = useAppUtils(); const intl = useIntl(); const [form] = Form.useForm(); const localPath = Form.useWatch('local_path', form); // Owned by the create-scope picker slot (admin "All" view). When set, scope // the worker picker to clusters owned by that org — a model file's owner is // derived from the target worker's cluster, so this keeps them aligned. const scopeOrgId = Form.useWatch('organization_id', form); const prevScopeRef = useRef(undefined); const visibleWorkerOptions = useMemo(() => { if (scopeOrgId == null) { return workerOptions; } return (workerOptions || []).filter( (cluster: any) => cluster.owner_principal_id === scopeOrgId ); }, [workerOptions, scopeOrgId]); useEffect(() => { const init = async () => { try { if (workersList && workersList?.length > 0) { const modelFiles = await getModelFileList(); generateWorkersModelFileOptions(modelFiles, workersList || []); } } catch (error) {} }; init(); }, [workersList]); // On a genuine org change, drop the now-out-of-scope worker selection. useEffect(() => { if (prevScopeRef.current === undefined) { prevScopeRef.current = scopeOrgId; return; } if (prevScopeRef.current === scopeOrgId) { return; } prevScopeRef.current = scopeOrgId; form.setFieldValue('worker_id', undefined); }, [scopeOrgId]); useImperativeHandle(ref, () => ({ form })); const handleOk = (values: any) => { const data = _.pickBy(values, (val: string) => val); onOk({ ...data, worker_id: data.worker_id?.[1] }); }; const handleOnLocalPathBlur = (e: any) => { let { value } = e.target; // remove all the backslashes and slashes at the end of the string value = value.replace(/(\\|\/)+$/, ''); form.setFieldsValue({ local_path: value }); }; const renderOptionNode = (props: { data: any }) => { const { data } = props; const currentWorker = modelFileOptions.find( (item) => item.value === data.value ); const isExisting = currentWorker?.children?.some((child) => { const isSameFile = child.fileName === (fileName || localPath || '') || minimatch(child.fileName || '', fileName || ''); return child.repoId === (selectedModel?.name || '') && isSameFile; }); const localeId = localPath ? 'resources.modelfiles.form.added' : 'resources.modelfiles.form.exsting'; return ( {data.label} {isExisting && ( [{intl.formatMessage({ id: localeId })}] )} ); }; const renderLocalPathFields = () => { return ( <> name="local_path" key="local_path" rules={[ { required: true, message: getRuleMessage('input', 'models.form.filePath') } ]} > } > ); }; const renderFieldsBySource = useMemo(() => { if (props.source === modelSourceMap.local_path_value) { return renderLocalPathFields(); } return null; }, [props.source, intl]); return (
name="source" rules={[ { required: true, message: getRuleMessage('select', 'models.form.source') } ]} > { } {renderFieldsBySource} triggerNode.parentNode} > {source !== modelSourceMap.local_path_value && ( name="local_dir" rules={[ { required: false, message: getRuleMessage( 'input', 'resources.modelfiles.form.localdir' ) } ]} > } label={intl.formatMessage({ id: 'resources.modelfiles.form.localdir' })} > )}
); }); export default TargetForm;