perf: vector format (#5516)

* perf: vector format

* feat: embedding batch size
This commit is contained in:
Archer
2025-08-22 10:18:24 +08:00
committed by GitHub
parent a92917c05f
commit 95325346ff
15 changed files with 436 additions and 38 deletions

View File

@@ -10,12 +10,14 @@ import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { ModelProviderList } from '@fastgpt/global/core/ai/provider';
import MultipleRowSelect from '@fastgpt/web/components/common/MySelect/MultipleRowSelect';
import { getModelFromList } from '@fastgpt/global/core/ai/model';
import type { ResponsiveValue } from '@chakra-ui/system';
type Props = SelectProps & {
disableTip?: string;
noOfLines?: ResponsiveValue<number>;
};
const OneRowSelector = ({ list, onChange, disableTip, ...props }: Props) => {
const OneRowSelector = ({ list, onChange, disableTip, noOfLines, ...props }: Props) => {
const { t } = useTranslation();
const { llmModelList, embeddingModelList, ttsModelList, sttModelList, reRankModelList } =
useSystemStore();
@@ -55,7 +57,7 @@ const OneRowSelector = ({ list, onChange, disableTip, ...props }: Props) => {
fallbackSrc={HUGGING_FACE_ICON}
/>
<Box noOfLines={1}>{modelData.name}</Box>
<Box noOfLines={noOfLines}>{modelData.name}</Box>
</Flex>
)
};
@@ -99,7 +101,14 @@ const OneRowSelector = ({ list, onChange, disableTip, ...props }: Props) => {
);
};
const MultipleRowSelector = ({ list, onChange, disableTip, placeholder, ...props }: Props) => {
const MultipleRowSelector = ({
list,
onChange,
disableTip,
placeholder,
noOfLines,
...props
}: Props) => {
const { t } = useTranslation();
const { llmModelList, embeddingModelList, ttsModelList, sttModelList, reRankModelList } =
useSystemStore();
@@ -189,7 +198,7 @@ const MultipleRowSelector = ({ list, onChange, disableTip, placeholder, ...props
fallbackSrc={HUGGING_FACE_ICON}
w={avatarSize}
/>
<Box noOfLines={1}>{modelData?.name}</Box>
<Box noOfLines={noOfLines}>{modelData?.name}</Box>
</Flex>
);
}, [modelList, props.value, t, avatarSize]);
@@ -222,7 +231,7 @@ const MultipleRowSelector = ({ list, onChange, disableTip, placeholder, ...props
};
const AIModelSelector = (props: Props) => {
return props.list.length > 100 ? (
return props.list.length > 10 ? (
<MultipleRowSelector {...props} />
) : (
<OneRowSelector {...props} />

View File

@@ -476,6 +476,26 @@ export const ModelEditModal = ({
</Flex>
</Td>
</Tr>
<Tr>
<Td>
<HStack spacing={1}>
<Box>{t('account_model:batch_size')}</Box>
</HStack>
</Td>
<Td textAlign={'right'}>
<Flex justifyContent={'flex-end'}>
<MyNumberInput
defaultValue={1}
register={register}
name="batchSize"
min={1}
step={1}
isRequired
{...InputStyles}
/>
</Flex>
</Td>
</Tr>
<Tr>
<Td>
<HStack spacing={1}>

View File

@@ -48,6 +48,7 @@ export const useDebug = () => {
const getNodes = useContextSelector(WorkflowNodeEdgeContext, (v) => v.getNodes);
const edges = useContextSelector(WorkflowNodeEdgeContext, (v) => v.edges);
const onUpdateNodeError = useContextSelector(WorkflowContext, (v) => v.onUpdateNodeError);
const onRemoveError = useContextSelector(WorkflowContext, (v) => v.onRemoveError);
const onStartNodeDebug = useContextSelector(WorkflowContext, (v) => v.onStartNodeDebug);
const appDetail = useContextSelector(AppContext, (v) => v.appDetail);
@@ -80,6 +81,7 @@ export const useDebug = () => {
const checkResults = checkWorkflowNodeAndConnection({ nodes, edges });
if (!checkResults) {
onRemoveError();
const storeNodes = uiWorkflow2StoreWorkflow({ nodes, edges });
return JSON.stringify(storeNodes);

View File

@@ -157,6 +157,7 @@ type WorkflowContextType = {
nodeList: FlowNodeItemType[];
onUpdateNodeError: (node: string, isError: Boolean) => void;
onRemoveError: () => void;
onResetNode: (e: { id: string; node: FlowNodeTemplateType }) => void;
onChangeNode: (e: FlowNodeChangeProps) => void;
getNodeDynamicInputs: (nodeId: string) => FlowNodeInputItemType[];
@@ -401,6 +402,9 @@ export const WorkflowContext = createContext<WorkflowContextType>({
isSaved?: boolean;
}): boolean {
throw new Error('Function not implemented.');
},
onRemoveError: function (): void {
throw new Error('Function not implemented.');
}
});
@@ -473,6 +477,17 @@ const WorkflowContextProvider = ({
});
});
});
const onRemoveError = useMemoizedFn(() => {
setNodes((state) => {
return state.map((item) => {
if (item.data.isError) {
item.data.isError = false;
item.selected = false;
}
return item;
});
});
});
// reset a node data. delete edge and replace it
const onResetNode = useMemoizedFn(({ id, node }: { id: string; node: FlowNodeTemplateType }) => {
@@ -625,6 +640,7 @@ const WorkflowContextProvider = ({
const checkResults = checkWorkflowNodeAndConnection({ nodes, edges });
if (!checkResults) {
onRemoveError();
const storeWorkflow = uiWorkflow2StoreWorkflow({ nodes, edges });
return storeWorkflow;
@@ -1025,6 +1041,7 @@ const WorkflowContextProvider = ({
// node
nodeList,
onUpdateNodeError,
onRemoveError,
onResetNode,
onChangeNode,
getNodeDynamicInputs,
@@ -1075,6 +1092,7 @@ const WorkflowContextProvider = ({
onChangeNode,
onDelEdge,
onNextNodeDebug,
onRemoveError,
onResetNode,
onStartNodeDebug,
onStopNodeDebug,

View File

@@ -248,6 +248,7 @@ const HomeChatWindow = ({ myApps }: Props) => {
size="sm"
bg={'myGray.50'}
rounded="full"
noOfLines={[1, 3]}
list={availableModels}
value={selectedModel}
onChange={async (model) => {

View File

@@ -381,6 +381,11 @@ export const getNodeAllSource = ({
};
/* ====== Connection ======= */
// Connectivity check result type
type ConnectivityIssue = {
nodeId: string;
issue: 'isolated' | 'no_input' | 'unreachable_from_start';
};
export const checkWorkflowNodeAndConnection = ({
nodes,
edges
@@ -388,7 +393,7 @@ export const checkWorkflowNodeAndConnection = ({
nodes: Node<FlowNodeItemType, string | undefined>[];
edges: Edge<any>[];
}): string[] | undefined => {
// 1. reference check. Required value
// Node check
for (const node of nodes) {
const data = node.data;
const inputs = data.inputs;
@@ -453,6 +458,15 @@ export const checkWorkflowNodeAndConnection = ({
return [data.nodeId];
}
}
if (data.flowNodeType === FlowNodeTypeEnum.agent) {
const toolConnections = edges.filter(
(edge) =>
edge.source === data.nodeId && edge.sourceHandle === NodeOutputKeyEnum.selectedTools
);
if (toolConnections.length === 0) {
return [data.nodeId];
}
}
// check node input
if (
@@ -506,7 +520,7 @@ export const checkWorkflowNodeAndConnection = ({
return [data.nodeId];
}
// filter tools node edge
// Check node has invalid edge
const edgeFilted = edges.filter(
(edge) =>
!(
@@ -514,7 +528,7 @@ export const checkWorkflowNodeAndConnection = ({
edge.sourceHandle === NodeOutputKeyEnum.selectedTools
)
);
// check node has edge
// Check node has edge
const hasEdge = edgeFilted.some(
(edge) => edge.source === data.nodeId || edge.target === data.nodeId
);
@@ -522,6 +536,106 @@ export const checkWorkflowNodeAndConnection = ({
return [data.nodeId];
}
}
// Edge check
/**
* Check graph connectivity and identify connectivity issues
*/
const checkConnectivity = (
nodes: Node<FlowNodeItemType, string | undefined>[],
edges: Edge<any>[]
): string[] => {
// Find start node
const startNode = nodes.find(
(node) =>
node.data.flowNodeType === FlowNodeTypeEnum.workflowStart ||
node.data.flowNodeType === FlowNodeTypeEnum.pluginInput
);
if (!startNode) {
// No start node found - this is a critical issue
return nodes.map((node) => node.data.nodeId);
}
const issues: ConnectivityIssue[] = [];
// Build adjacency lists for both directions
const outgoing = new Map<string, string[]>();
const incoming = new Map<string, string[]>();
nodes.forEach((node) => {
outgoing.set(node.data.nodeId, []);
incoming.set(node.data.nodeId, []);
});
edges.forEach((edge) => {
const outList = outgoing.get(edge.source) || [];
outList.push(edge.target);
outgoing.set(edge.source, outList);
const inList = incoming.get(edge.target) || [];
inList.push(edge.source);
incoming.set(edge.target, inList);
});
// Check reachability from start nodeStart node/Loop start 可以到达的地方)
const reachableFromStart = new Set<string>();
const dfsFromStart = (nodeId: string) => {
if (reachableFromStart.has(nodeId)) return;
reachableFromStart.add(nodeId);
const neighbors = outgoing.get(nodeId) || [];
neighbors.forEach((neighbor) => dfsFromStart(neighbor));
};
dfsFromStart(startNode.data.nodeId);
nodes.forEach((node) => {
if (node.data.flowNodeType === FlowNodeTypeEnum.loopStart) {
dfsFromStart(node.data.nodeId);
}
});
// Check each node for connectivity issues
for (const node of nodes) {
const nodeId = node.data.nodeId;
const nodeType = node.data.flowNodeType;
// Skip system nodes that don't need connectivity checks
if (
nodeType === FlowNodeTypeEnum.systemConfig ||
nodeType === FlowNodeTypeEnum.pluginConfig ||
nodeType === FlowNodeTypeEnum.comment ||
nodeType === FlowNodeTypeEnum.globalVariable ||
nodeType === FlowNodeTypeEnum.emptyNode
) {
continue;
}
const hasIncoming = (incoming.get(nodeId) || []).length > 0;
const hasOutgoing = (outgoing.get(nodeId) || []).length > 0;
const isStartNode = [
FlowNodeTypeEnum.workflowStart,
FlowNodeTypeEnum.pluginInput,
FlowNodeTypeEnum.loopStart
].includes(nodeType);
// Check if node is reachable from start
if (!isStartNode && !reachableFromStart.has(nodeId)) {
issues.push({
nodeId,
issue: 'unreachable_from_start'
});
break;
}
}
return issues.map((issue) => issue.nodeId);
};
const connectivityIssues = checkConnectivity(nodes, edges);
if (connectivityIssues.length > 0) {
return connectivityIssues;
}
};
/* ====== Variables ======= */