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:
Archer
2024-11-13 11:29:53 +08:00
committed by GitHub
parent e1f5483432
commit e9d52ada73
449 changed files with 7626 additions and 4180 deletions

View File

@@ -22,7 +22,9 @@ export const defaultApp: AppDetailType = {
export const defaultOutLinkForm: OutLinkEditType = {
name: '',
showNodeStatus: true,
responseDetail: false,
showRawSource: false,
limit: {
QPM: 100,
maxUsagePoints: -1

View File

@@ -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);
})();

View File

@@ -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;

View File

@@ -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);

View File

@@ -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');

View File

@@ -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;
})