4.8.13 feature (#3118)
* chore(ui): login page & workflow page (#3046) * login page & number input & multirow select & llm select * workflow * adjust nodes * New file upload (#3058) * feat: toolNode aiNode readFileNode adapt new version * update docker-compose * update tip * feat: adapt new file version * perf: file input * fix: ts * feat: add chat history time label (#3024) * feat:add chat and logs time * feat: add chat history time label * code perf * code perf --------- Co-authored-by: 勤劳上班的卑微小张 <jiazhan.zhang@ggimage.com> * add chatType (#3060) * pref: slow query of full text search (#3044) * Adapt findLast api;perf: markdown zh format. (#3066) * perf: context code * fix: adapt findLast api * perf: commercial plugin run error * perf: markdown zh format * perf: dockerfile proxy (#3067) * fix ui (#3065) * fix ui * fix * feat: support array reference multi-select (#3041) * feat: support array reference multi-select * fix build * fix * fix loop multi-select * adjust condition * fix get value * array and non-array conversion * fix plugin input * merge func * feat: iframe code block;perf: workflow selector type (#3076) * feat: iframe code block * perf: workflow selector type * node pluginoutput check (#3074) * feat: View will move when workflow check error;fix: ui refresh error when continuous file upload (#3077) * fix: plugin output check * fix: ui refresh error when continuous file upload * feat: View will move when workflow check error * add dispatch try catch (#3075) * perf: workflow context split (#3083) * perf: workflow context split * perf: context * 4.8.13 test (#3085) * perf: workflow node ui * chat iframe url * feat: support sub route config (#3071) * feat: support sub route config * dockerfile * fix upload * delete unused code * 4.8.13 test (#3087) * fix: image expired * fix: datacard navbar ui * perf: build action * fix: workflow file upload refresh (#3088) * fix: http tool response (#3097) * loop node dynamic height (#3092) * loop node dynamic height * fix * fix * feat: support push chat log (#3093) * feat: custom uid/metadata * to: custom info * fix: chat push latest * feat: add chat log envs * refactor: move timer to pushChatLog * fix: using precise log --------- Co-authored-by: Finley Ge <m13203533462@163.com> * 4.8.13 test (#3098) * perf: loop node refresh * rename context * comment * fix: ts * perf: push chat log * array reference check & node ui (#3100) * feat: loop start add index (#3101) * feat: loop start add index * update doc * 4.8.13 test (#3102) * fix: loop index;edge parent check * perf: reference invalid check * fix: ts * fix: plugin select files and ai response check (#3104) * fix: plugin select files and ai response check * perf: text editor selector;tool call tip;remove invalid image url; * perf: select file * perf: drop files * feat: source id prefix env (#3103) * 4.8.13 test (#3106) * perf: select file * perf: drop files * perf: env template * 4.8.13 test (#3107) * perf: select file * perf: drop files * fix: imple mode adapt files * perf: push chat log (#3109) * fix: share page load title error (#3111) * 4.8.13 perf (#3112) * fix: share page load title error * update file input doc * perf: auto add file urls * perf: auto ser loop node offset height * 4.8.13 test (#3117) * perf: plugin * updat eaction * feat: add more share config (#3120) * feat: add more share config * add i18n en * fix: missing subroute (#3121) * perf: outlink config (#3128) * update action * perf: outlink config * fix: ts (#3129) * 更新 docSite 文档内容 (#3131) * fix: null pointer (#3130) * fix: null pointer * perf: not input text * update doc url * perf: outlink default value (#3134) * update doc (#3136) * 4.8.13 test (#3137) * update doc * perf: completions chat api * Restore docSite content based on upstream/4.8.13-dev (#3138) * Restore docSite content based on upstream/4.8.13-dev * 4813.md缺少更正 * update doc (#3141) --------- Co-authored-by: heheer <heheer@sealos.io> Co-authored-by: papapatrick <109422393+Patrickill@users.noreply.github.com> Co-authored-by: 勤劳上班的卑微小张 <jiazhan.zhang@ggimage.com> Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com> Co-authored-by: a.e. <49438478+I-Info@users.noreply.github.com> Co-authored-by: Finley Ge <m13203533462@163.com> Co-authored-by: Jiangween <145003935+Jiangween@users.noreply.github.com>
This commit is contained in:
@@ -22,7 +22,9 @@ export const defaultApp: AppDetailType = {
|
||||
|
||||
export const defaultOutLinkForm: OutLinkEditType = {
|
||||
name: '',
|
||||
showNodeStatus: true,
|
||||
responseDetail: false,
|
||||
showRawSource: false,
|
||||
limit: {
|
||||
QPM: 100,
|
||||
maxUsagePoints: -1
|
||||
|
||||
@@ -11,7 +11,11 @@ import {
|
||||
FlowNodeInputTypeEnum,
|
||||
FlowNodeTypeEnum
|
||||
} from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { NodeInputKeyEnum, WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import {
|
||||
NodeInputKeyEnum,
|
||||
NodeOutputKeyEnum,
|
||||
WorkflowIOValueTypeEnum
|
||||
} from '@fastgpt/global/core/workflow/constants';
|
||||
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
||||
@@ -30,9 +34,11 @@ import {
|
||||
AiChatQuoteTemplate
|
||||
} from '@fastgpt/global/core/workflow/template/system/aiChat/index';
|
||||
import { DatasetSearchModule } from '@fastgpt/global/core/workflow/template/system/datasetSearch';
|
||||
import { ReadFilesNode } from '@fastgpt/global/core/workflow/template/system/readFiles';
|
||||
import { i18nT } from '@fastgpt/web/i18n/utils';
|
||||
import { Input_Template_UserChatInput } from '@fastgpt/global/core/workflow/template/input';
|
||||
import {
|
||||
Input_Template_File_Link_Prompt,
|
||||
Input_Template_UserChatInput
|
||||
} from '@fastgpt/global/core/workflow/template/input';
|
||||
|
||||
type WorkflowType = {
|
||||
nodes: StoreNodeItemType[];
|
||||
@@ -173,6 +179,10 @@ export function form2AppWorkflow(
|
||||
valueType: WorkflowIOValueTypeEnum.datasetQuote,
|
||||
value: selectedDatasets?.length > 0 ? [datasetNodeId, 'quoteQA'] : undefined
|
||||
},
|
||||
{
|
||||
...Input_Template_File_Link_Prompt,
|
||||
value: [[workflowStartNodeId, NodeOutputKeyEnum.userFiles]]
|
||||
},
|
||||
{
|
||||
key: NodeInputKeyEnum.aiChatVision,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
||||
@@ -188,7 +198,7 @@ export function form2AppWorkflow(
|
||||
return {
|
||||
nodeId: datasetNodeId,
|
||||
name: t(DatasetSearchModule.name),
|
||||
intro: t(DatasetSearchModule.intro),
|
||||
intro: t('app:dataset_search_tool_description'),
|
||||
avatar: DatasetSearchModule.avatar,
|
||||
flowNodeType: DatasetSearchModule.flowNodeType,
|
||||
showStatus: true,
|
||||
@@ -321,44 +331,6 @@ export function form2AppWorkflow(
|
||||
]
|
||||
}
|
||||
: null;
|
||||
// Read file tool config
|
||||
const readFileTool: WorkflowType | null = data.chatConfig.fileSelectConfig?.canSelectFile
|
||||
? {
|
||||
nodes: [
|
||||
{
|
||||
nodeId: ReadFilesNode.id,
|
||||
name: t(ReadFilesNode.name),
|
||||
intro: t(ReadFilesNode.intro),
|
||||
avatar: ReadFilesNode.avatar,
|
||||
flowNodeType: ReadFilesNode.flowNodeType,
|
||||
showStatus: true,
|
||||
position: {
|
||||
x: 974.6209854328943,
|
||||
y: 587.6378828744465
|
||||
},
|
||||
version: ReadFilesNode.version,
|
||||
inputs: [
|
||||
{
|
||||
key: NodeInputKeyEnum.fileUrlList,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.reference],
|
||||
valueType: WorkflowIOValueTypeEnum.arrayString,
|
||||
label: t('app:workflow.file_url'),
|
||||
value: [workflowStartNodeId, 'userFiles']
|
||||
}
|
||||
],
|
||||
outputs: ReadFilesNode.outputs
|
||||
}
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
source: toolNodeId,
|
||||
target: ReadFilesNode.id,
|
||||
sourceHandle: 'selectedTools',
|
||||
targetHandle: 'selectedTools'
|
||||
}
|
||||
]
|
||||
}
|
||||
: null;
|
||||
|
||||
// Computed tools config
|
||||
const pluginTool: WorkflowType[] = formData.selectedTools.map((tool, i) => {
|
||||
@@ -477,6 +449,10 @@ export function form2AppWorkflow(
|
||||
max: 30,
|
||||
value: formData.aiSettings.maxHistories
|
||||
},
|
||||
{
|
||||
...Input_Template_File_Link_Prompt,
|
||||
value: [[workflowStartNodeId, NodeOutputKeyEnum.userFiles]]
|
||||
},
|
||||
{
|
||||
key: 'userChatInput',
|
||||
renderTypeList: [FlowNodeInputTypeEnum.reference, FlowNodeInputTypeEnum.textarea],
|
||||
@@ -497,7 +473,6 @@ export function form2AppWorkflow(
|
||||
},
|
||||
// tool nodes
|
||||
...(datasetTool ? datasetTool.nodes : []),
|
||||
...(readFileTool ? readFileTool.nodes : []),
|
||||
...pluginTool.map((tool) => tool.nodes).flat()
|
||||
],
|
||||
edges: [
|
||||
@@ -509,7 +484,6 @@ export function form2AppWorkflow(
|
||||
},
|
||||
// tool edges
|
||||
...(datasetTool ? datasetTool.edges : []),
|
||||
...(readFileTool ? readFileTool.edges : []),
|
||||
...pluginTool.map((tool) => tool.edges).flat()
|
||||
]
|
||||
};
|
||||
@@ -530,8 +504,7 @@ export function form2AppWorkflow(
|
||||
}
|
||||
|
||||
const workflow = (() => {
|
||||
if (data.selectedTools.length > 0 || data.chatConfig.fileSelectConfig?.canSelectFile)
|
||||
return toolTemplates(data);
|
||||
if (data.selectedTools.length > 0) return toolTemplates(data);
|
||||
if (selectedDatasets.length > 0) return datasetTemplate(data);
|
||||
return simpleChatTemplate(data);
|
||||
})();
|
||||
|
||||
@@ -16,7 +16,7 @@ import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';
|
||||
|
||||
type ChatContextValueType = {
|
||||
params: Record<string, string | number>;
|
||||
params: Record<string, string | number | boolean>;
|
||||
};
|
||||
type ChatContextType = {
|
||||
chatId: string;
|
||||
|
||||
@@ -34,10 +34,7 @@ import type { CreateDatasetParams, InsertOneDatasetDataProps } from '@/global/co
|
||||
import type { DatasetCollectionItemType } from '@fastgpt/global/core/dataset/type';
|
||||
import { DatasetCollectionSyncResultEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import type { DatasetDataItemType } from '@fastgpt/global/core/dataset/type';
|
||||
import type {
|
||||
DatasetCollectionsListItemType,
|
||||
DatasetDataListItemType
|
||||
} from '@/global/core/dataset/type.d';
|
||||
import type { DatasetCollectionsListItemType } from '@/global/core/dataset/type.d';
|
||||
import { PagingData } from '@/types';
|
||||
import type { getDatasetTrainingQueueResponse } from '@/pages/api/core/dataset/training/getDatasetTrainingQueue';
|
||||
import type { rebuildEmbeddingBody } from '@/pages/api/core/dataset/training/rebuildEmbedding';
|
||||
@@ -45,7 +42,10 @@ import type {
|
||||
PostPreviewFilesChunksProps,
|
||||
PreviewChunksResponse
|
||||
} from '@/pages/api/core/dataset/file/getPreviewChunks';
|
||||
import type { readCollectionSourceResponse } from '@/pages/api/core/dataset/collection/read';
|
||||
import type {
|
||||
readCollectionSourceBody,
|
||||
readCollectionSourceResponse
|
||||
} from '@/pages/api/core/dataset/collection/read';
|
||||
import type { GetDatasetListBody } from '@/pages/api/core/dataset/list';
|
||||
import type { UpdateDatasetCollectionParams } from '@/pages/api/core/dataset/collection/update';
|
||||
import type {
|
||||
@@ -197,5 +197,5 @@ export const getPreviewChunks = (data: PostPreviewFilesChunksProps) =>
|
||||
POST<PreviewChunksResponse>('/core/dataset/file/getPreviewChunks', data);
|
||||
|
||||
/* ================== read source ======================== */
|
||||
export const getCollectionSource = (collectionId: string) =>
|
||||
GET<readCollectionSourceResponse>('/core/dataset/collection/read', { collectionId });
|
||||
export const getCollectionSource = (data: readCollectionSourceBody) =>
|
||||
POST<readCollectionSourceResponse>('/core/dataset/collection/read', data);
|
||||
|
||||
@@ -3,8 +3,15 @@ import { getCollectionSource } from '@/web/core/dataset/api';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { ShareChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
||||
|
||||
export function getCollectionSourceAndOpen(collectionId: string) {
|
||||
export function getCollectionSourceAndOpen({
|
||||
collectionId,
|
||||
shareId,
|
||||
outLinkUid
|
||||
}: {
|
||||
collectionId: string;
|
||||
} & ShareChatAuthProps) {
|
||||
const { toast } = useToast();
|
||||
const { t } = useTranslation();
|
||||
const { setLoading } = useSystemStore();
|
||||
@@ -12,7 +19,8 @@ export function getCollectionSourceAndOpen(collectionId: string) {
|
||||
return async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const { value: url } = await getCollectionSource(collectionId);
|
||||
|
||||
const { value: url } = await getCollectionSource({ collectionId, shareId, outLinkUid });
|
||||
|
||||
if (!url) {
|
||||
throw new Error('No file found');
|
||||
|
||||
@@ -16,19 +16,21 @@ import { EmptyNode } from '@fastgpt/global/core/workflow/template/system/emptyNo
|
||||
import { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import { getGlobalVariableNode } from './adapt';
|
||||
import { VARIABLE_NODE_ID, WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { EditorVariablePickerType } from '@fastgpt/web/components/common/Textarea/PromptEditor/type';
|
||||
import {
|
||||
formatEditorVariablePickerIcon,
|
||||
getAppChatConfig,
|
||||
getGuideModule
|
||||
getGuideModule,
|
||||
isValidArrayReferenceValue,
|
||||
isValidReferenceValue
|
||||
} from '@fastgpt/global/core/workflow/utils';
|
||||
import { TFunction } from 'next-i18next';
|
||||
import {
|
||||
FlowNodeInputItemType,
|
||||
FlowNodeOutputItemType,
|
||||
ReferenceValueProps
|
||||
ReferenceItemValueType
|
||||
} from '@fastgpt/global/core/workflow/type/io';
|
||||
import { IfElseListItemType } from '@fastgpt/global/core/workflow/template/system/ifElse/type';
|
||||
import { VariableConditionEnum } from '@fastgpt/global/core/workflow/template/system/ifElse/constant';
|
||||
@@ -227,7 +229,7 @@ export const getRefData = ({
|
||||
nodeList,
|
||||
chatConfig
|
||||
}: {
|
||||
variable?: ReferenceValueProps;
|
||||
variable?: ReferenceItemValueType;
|
||||
nodeList: FlowNodeItemType[];
|
||||
chatConfig: AppChatConfigType;
|
||||
}) => {
|
||||
@@ -261,6 +263,75 @@ export const getRefData = ({
|
||||
};
|
||||
};
|
||||
|
||||
// 根据数据类型,过滤无效的节点输出
|
||||
export const filterWorkflowNodeOutputsByType = (
|
||||
outputs: FlowNodeOutputItemType[],
|
||||
valueType: WorkflowIOValueTypeEnum
|
||||
): FlowNodeOutputItemType[] => {
|
||||
const validTypeMap: Record<WorkflowIOValueTypeEnum, WorkflowIOValueTypeEnum[]> = {
|
||||
[WorkflowIOValueTypeEnum.string]: [WorkflowIOValueTypeEnum.string],
|
||||
[WorkflowIOValueTypeEnum.number]: [WorkflowIOValueTypeEnum.number],
|
||||
[WorkflowIOValueTypeEnum.boolean]: [WorkflowIOValueTypeEnum.boolean],
|
||||
[WorkflowIOValueTypeEnum.object]: [WorkflowIOValueTypeEnum.object],
|
||||
[WorkflowIOValueTypeEnum.arrayString]: [
|
||||
WorkflowIOValueTypeEnum.string,
|
||||
WorkflowIOValueTypeEnum.arrayString,
|
||||
WorkflowIOValueTypeEnum.arrayAny
|
||||
],
|
||||
[WorkflowIOValueTypeEnum.arrayNumber]: [
|
||||
WorkflowIOValueTypeEnum.number,
|
||||
WorkflowIOValueTypeEnum.arrayNumber,
|
||||
WorkflowIOValueTypeEnum.arrayAny
|
||||
],
|
||||
[WorkflowIOValueTypeEnum.arrayBoolean]: [
|
||||
WorkflowIOValueTypeEnum.boolean,
|
||||
WorkflowIOValueTypeEnum.arrayBoolean,
|
||||
WorkflowIOValueTypeEnum.arrayAny
|
||||
],
|
||||
[WorkflowIOValueTypeEnum.arrayObject]: [
|
||||
WorkflowIOValueTypeEnum.object,
|
||||
WorkflowIOValueTypeEnum.arrayObject,
|
||||
WorkflowIOValueTypeEnum.arrayAny,
|
||||
WorkflowIOValueTypeEnum.chatHistory,
|
||||
WorkflowIOValueTypeEnum.datasetQuote,
|
||||
WorkflowIOValueTypeEnum.dynamic,
|
||||
WorkflowIOValueTypeEnum.selectDataset,
|
||||
WorkflowIOValueTypeEnum.selectApp
|
||||
],
|
||||
[WorkflowIOValueTypeEnum.chatHistory]: [
|
||||
WorkflowIOValueTypeEnum.chatHistory,
|
||||
WorkflowIOValueTypeEnum.arrayAny
|
||||
],
|
||||
[WorkflowIOValueTypeEnum.datasetQuote]: [
|
||||
WorkflowIOValueTypeEnum.datasetQuote,
|
||||
WorkflowIOValueTypeEnum.arrayAny
|
||||
],
|
||||
[WorkflowIOValueTypeEnum.dynamic]: [
|
||||
WorkflowIOValueTypeEnum.dynamic,
|
||||
WorkflowIOValueTypeEnum.arrayAny
|
||||
],
|
||||
[WorkflowIOValueTypeEnum.selectDataset]: [
|
||||
WorkflowIOValueTypeEnum.selectDataset,
|
||||
WorkflowIOValueTypeEnum.arrayAny
|
||||
],
|
||||
[WorkflowIOValueTypeEnum.selectApp]: [
|
||||
WorkflowIOValueTypeEnum.selectApp,
|
||||
WorkflowIOValueTypeEnum.arrayAny
|
||||
],
|
||||
[WorkflowIOValueTypeEnum.arrayAny]: [WorkflowIOValueTypeEnum.arrayAny],
|
||||
[WorkflowIOValueTypeEnum.any]: [WorkflowIOValueTypeEnum.arrayAny]
|
||||
};
|
||||
|
||||
return outputs.filter(
|
||||
(output) =>
|
||||
valueType === WorkflowIOValueTypeEnum.any ||
|
||||
valueType === WorkflowIOValueTypeEnum.arrayAny ||
|
||||
!output.valueType ||
|
||||
output.valueType === WorkflowIOValueTypeEnum.any ||
|
||||
validTypeMap[valueType].includes(output.valueType)
|
||||
);
|
||||
};
|
||||
|
||||
/* Connection rules */
|
||||
export const checkWorkflowNodeAndConnection = ({
|
||||
nodes,
|
||||
@@ -269,6 +340,7 @@ export const checkWorkflowNodeAndConnection = ({
|
||||
nodes: Node<FlowNodeItemType, string | undefined>[];
|
||||
edges: Edge<any>[];
|
||||
}): string[] | undefined => {
|
||||
const nodeIds: string[] = nodes.map((node) => node.data.nodeId);
|
||||
// 1. reference check. Required value
|
||||
for (const node of nodes) {
|
||||
const data = node.data;
|
||||
@@ -324,27 +396,28 @@ export const checkWorkflowNodeAndConnection = ({
|
||||
if (input.value === undefined) return true;
|
||||
}
|
||||
|
||||
// Check plugin output
|
||||
// if (
|
||||
// node.data.flowNodeType === FlowNodeTypeEnum.pluginOutput &&
|
||||
// (input.value?.length === 0 ||
|
||||
// (isValidReferenceValue(input.value, nodeIds) && !input.value?.[1]))
|
||||
// ) {
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// check reference invalid
|
||||
const renderType = input.renderTypeList[input.selectedTypeIndex || 0];
|
||||
if (renderType === FlowNodeInputTypeEnum.reference && input.required) {
|
||||
if (!input.value || !Array.isArray(input.value) || input.value.length !== 2) {
|
||||
return true;
|
||||
if (renderType === FlowNodeInputTypeEnum.reference) {
|
||||
if (input.valueType?.startsWith('array')) {
|
||||
if (input.required && (!input.value || input.value.length === 0)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !isValidArrayReferenceValue(input.value, nodeIds);
|
||||
}
|
||||
|
||||
// variable key not need to check
|
||||
if (input.value[0] === VARIABLE_NODE_ID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Can not find key
|
||||
const sourceNode = nodes.find((item) => item.data.nodeId === input.value[0]);
|
||||
if (!sourceNode) {
|
||||
return true;
|
||||
}
|
||||
const sourceOutput = sourceNode.data.outputs.find((item) => item.id === input.value[1]);
|
||||
if (!sourceOutput) {
|
||||
return true;
|
||||
}
|
||||
// Single reference
|
||||
return input.required && !isValidReferenceValue(input.value, nodeIds);
|
||||
}
|
||||
return false;
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user