Co-authored-by: hobo.l <hobo.l@binance.com> Co-authored-by: crazywoola <427733928@qq.com>tags/0.15.0
| @@ -2,7 +2,7 @@ import json | |||
| import logging | |||
| from flask import abort, request | |||
| from flask_restful import Resource, marshal_with, reqparse # type: ignore | |||
| from flask_restful import Resource, inputs, marshal_with, reqparse # type: ignore | |||
| from werkzeug.exceptions import Forbidden, InternalServerError, NotFound | |||
| import services | |||
| @@ -14,7 +14,7 @@ from controllers.console.wraps import account_initialization_required, setup_req | |||
| from core.app.apps.base_app_queue_manager import AppQueueManager | |||
| from core.app.entities.app_invoke_entities import InvokeFrom | |||
| from factories import variable_factory | |||
| from fields.workflow_fields import workflow_fields | |||
| from fields.workflow_fields import workflow_fields, workflow_pagination_fields | |||
| from fields.workflow_run_fields import workflow_run_node_execution_fields | |||
| from libs import helper | |||
| from libs.helper import TimestampField, uuid_value | |||
| @@ -440,6 +440,31 @@ class WorkflowConfigApi(Resource): | |||
| } | |||
| class PublishedAllWorkflowApi(Resource): | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| @get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW]) | |||
| @marshal_with(workflow_pagination_fields) | |||
| def get(self, app_model: App): | |||
| """ | |||
| Get published workflows | |||
| """ | |||
| if not current_user.is_editor: | |||
| raise Forbidden() | |||
| parser = reqparse.RequestParser() | |||
| parser.add_argument("page", type=inputs.int_range(1, 99999), required=False, default=1, location="args") | |||
| parser.add_argument("limit", type=inputs.int_range(1, 100), required=False, default=20, location="args") | |||
| args = parser.parse_args() | |||
| page = args.get("page") | |||
| limit = args.get("limit") | |||
| workflow_service = WorkflowService() | |||
| workflows, has_more = workflow_service.get_all_published_workflow(app_model=app_model, page=page, limit=limit) | |||
| return {"items": workflows, "page": page, "limit": limit, "has_more": has_more} | |||
| api.add_resource(DraftWorkflowApi, "/apps/<uuid:app_id>/workflows/draft") | |||
| api.add_resource(WorkflowConfigApi, "/apps/<uuid:app_id>/workflows/draft/config") | |||
| api.add_resource(AdvancedChatDraftWorkflowRunApi, "/apps/<uuid:app_id>/advanced-chat/workflows/draft/run") | |||
| @@ -454,6 +479,7 @@ api.add_resource( | |||
| WorkflowDraftRunIterationNodeApi, "/apps/<uuid:app_id>/workflows/draft/iteration/nodes/<string:node_id>/run" | |||
| ) | |||
| api.add_resource(PublishedWorkflowApi, "/apps/<uuid:app_id>/workflows/publish") | |||
| api.add_resource(PublishedAllWorkflowApi, "/apps/<uuid:app_id>/workflows") | |||
| api.add_resource(DefaultBlockConfigsApi, "/apps/<uuid:app_id>/workflows/default-workflow-block-configs") | |||
| api.add_resource( | |||
| DefaultBlockConfigApi, "/apps/<uuid:app_id>/workflows/default-workflow-block-configs/<string:block_type>" | |||
| @@ -45,6 +45,7 @@ workflow_fields = { | |||
| "graph": fields.Raw(attribute="graph_dict"), | |||
| "features": fields.Raw(attribute="features_dict"), | |||
| "hash": fields.String(attribute="unique_hash"), | |||
| "version": fields.String(attribute="version"), | |||
| "created_by": fields.Nested(simple_account_fields, attribute="created_by_account"), | |||
| "created_at": TimestampField, | |||
| "updated_by": fields.Nested(simple_account_fields, attribute="updated_by_account", allow_null=True), | |||
| @@ -61,3 +62,10 @@ workflow_partial_fields = { | |||
| "updated_by": fields.String, | |||
| "updated_at": TimestampField, | |||
| } | |||
| workflow_pagination_fields = { | |||
| "items": fields.List(fields.Nested(workflow_fields), attribute="items"), | |||
| "page": fields.Integer, | |||
| "limit": fields.Integer(attribute="limit"), | |||
| "has_more": fields.Boolean(attribute="has_more"), | |||
| } | |||
| @@ -5,6 +5,8 @@ from datetime import UTC, datetime | |||
| from typing import Any, Optional, cast | |||
| from uuid import uuid4 | |||
| from sqlalchemy import desc | |||
| from core.app.apps.advanced_chat.app_config_manager import AdvancedChatAppConfigManager | |||
| from core.app.apps.workflow.app_config_manager import WorkflowAppConfigManager | |||
| from core.model_runtime.utils.encoders import jsonable_encoder | |||
| @@ -76,6 +78,28 @@ class WorkflowService: | |||
| return workflow | |||
| def get_all_published_workflow(self, app_model: App, page: int, limit: int) -> tuple[list[Workflow], bool]: | |||
| """ | |||
| Get published workflow with pagination | |||
| """ | |||
| if not app_model.workflow_id: | |||
| return [], False | |||
| workflows = ( | |||
| db.session.query(Workflow) | |||
| .filter(Workflow.app_id == app_model.id) | |||
| .order_by(desc(Workflow.version)) | |||
| .offset((page - 1) * limit) | |||
| .limit(limit + 1) | |||
| .all() | |||
| ) | |||
| has_more = len(workflows) > limit | |||
| if has_more: | |||
| workflows = workflows[:-1] | |||
| return workflows, has_more | |||
| def sync_draft_workflow( | |||
| self, | |||
| *, | |||
| @@ -20,6 +20,7 @@ import type { StartNodeType } from '../nodes/start/types' | |||
| import { | |||
| useChecklistBeforePublish, | |||
| useIsChatMode, | |||
| useNodesInteractions, | |||
| useNodesReadOnly, | |||
| useNodesSyncDraft, | |||
| useWorkflowMode, | |||
| @@ -35,6 +36,7 @@ import RestoringTitle from './restoring-title' | |||
| import ViewHistory from './view-history' | |||
| import ChatVariableButton from './chat-variable-button' | |||
| import EnvButton from './env-button' | |||
| import VersionHistoryModal from './version-history-modal' | |||
| import Button from '@/app/components/base/button' | |||
| import { useStore as useAppStore } from '@/app/components/app/store' | |||
| import { publishWorkflow } from '@/service/workflow' | |||
| @@ -49,11 +51,13 @@ const Header: FC = () => { | |||
| const appID = appDetail?.id | |||
| const isChatMode = useIsChatMode() | |||
| const { nodesReadOnly, getNodesReadOnly } = useNodesReadOnly() | |||
| const { handleNodeSelect } = useNodesInteractions() | |||
| const publishedAt = useStore(s => s.publishedAt) | |||
| const draftUpdatedAt = useStore(s => s.draftUpdatedAt) | |||
| const toolPublished = useStore(s => s.toolPublished) | |||
| const nodes = useNodes<StartNodeType>() | |||
| const startNode = nodes.find(node => node.data.type === BlockEnum.Start) | |||
| const selectedNode = nodes.find(node => node.data.selected) | |||
| const startVariables = startNode?.data.variables | |||
| const fileSettings = useFeatures(s => s.features.file) | |||
| const variables = useMemo(() => { | |||
| @@ -76,7 +80,6 @@ const Header: FC = () => { | |||
| const { | |||
| handleLoadBackupDraft, | |||
| handleBackupDraft, | |||
| handleRestoreFromPublishedWorkflow, | |||
| } = useWorkflowRun() | |||
| const { handleCheckBeforePublish } = useChecklistBeforePublish() | |||
| const { handleSyncWorkflowDraft } = useNodesSyncDraft() | |||
| @@ -126,8 +129,10 @@ const Header: FC = () => { | |||
| const onStartRestoring = useCallback(() => { | |||
| workflowStore.setState({ isRestoring: true }) | |||
| handleBackupDraft() | |||
| handleRestoreFromPublishedWorkflow() | |||
| }, [handleBackupDraft, handleRestoreFromPublishedWorkflow, workflowStore]) | |||
| // clear right panel | |||
| if (selectedNode) | |||
| handleNodeSelect(selectedNode.id, true) | |||
| }, [handleBackupDraft, workflowStore, handleNodeSelect, selectedNode]) | |||
| const onPublisherToggle = useCallback((state: boolean) => { | |||
| if (state) | |||
| @@ -209,23 +214,27 @@ const Header: FC = () => { | |||
| } | |||
| { | |||
| restoring && ( | |||
| <div className='flex items-center space-x-2'> | |||
| <Button className='text-components-button-secondary-text' onClick={handleShowFeatures}> | |||
| <RiApps2AddLine className='w-4 h-4 mr-1 text-components-button-secondary-text' /> | |||
| {t('workflow.common.features')} | |||
| </Button> | |||
| <Divider type='vertical' className='h-3.5 mx-auto' /> | |||
| <Button | |||
| onClick={handleCancelRestore} | |||
| > | |||
| {t('common.operation.cancel')} | |||
| </Button> | |||
| <Button | |||
| onClick={handleRestore} | |||
| variant='primary' | |||
| > | |||
| {t('workflow.common.restore')} | |||
| </Button> | |||
| <div className='flex flex-col mt-auto'> | |||
| <div className='flex items-center justify-end my-4'> | |||
| <Button className='text-components-button-secondary-text' onClick={handleShowFeatures}> | |||
| <RiApps2AddLine className='w-4 h-4 mr-1 text-components-button-secondary-text' /> | |||
| {t('workflow.common.features')} | |||
| </Button> | |||
| <div className='mx-2 w-[1px] h-3.5 bg-gray-200'></div> | |||
| <Button | |||
| className='mr-2' | |||
| onClick={handleCancelRestore} | |||
| > | |||
| {t('common.operation.cancel')} | |||
| </Button> | |||
| <Button | |||
| onClick={handleRestore} | |||
| variant='primary' | |||
| > | |||
| {t('workflow.common.restore')} | |||
| </Button> | |||
| </div> | |||
| <VersionHistoryModal /> | |||
| </div> | |||
| ) | |||
| } | |||
| @@ -0,0 +1,66 @@ | |||
| import React from 'react' | |||
| import dayjs from 'dayjs' | |||
| import { useTranslation } from 'react-i18next' | |||
| import { WorkflowVersion } from '../types' | |||
| import cn from '@/utils/classnames' | |||
| import type { VersionHistory } from '@/types/workflow' | |||
| type VersionHistoryItemProps = { | |||
| item: VersionHistory | |||
| selectedVersion: string | |||
| onClick: (item: VersionHistory) => void | |||
| curIdx: number | |||
| page: number | |||
| } | |||
| const formatVersion = (version: string, curIdx: number, page: number): string => { | |||
| if (curIdx === 0 && page === 1) | |||
| return WorkflowVersion.Draft | |||
| if (curIdx === 1 && page === 1) | |||
| return WorkflowVersion.Latest | |||
| try { | |||
| const date = new Date(version) | |||
| if (isNaN(date.getTime())) | |||
| return version | |||
| // format as YYYY-MM-DD HH:mm:ss | |||
| return date.toISOString().slice(0, 19).replace('T', ' ') | |||
| } | |||
| catch { | |||
| return version | |||
| } | |||
| } | |||
| const VersionHistoryItem: React.FC<VersionHistoryItemProps> = ({ item, selectedVersion, onClick, curIdx, page }) => { | |||
| const { t } = useTranslation() | |||
| const formatTime = (time: number) => dayjs.unix(time).format('YYYY-MM-DD HH:mm:ss') | |||
| const formattedVersion = formatVersion(item.version, curIdx, page) | |||
| const renderVersionLabel = (version: string) => ( | |||
| (version === WorkflowVersion.Draft || version === WorkflowVersion.Latest) | |||
| ? ( | |||
| <div className="shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate"> | |||
| {version} | |||
| </div> | |||
| ) | |||
| : null | |||
| ) | |||
| return ( | |||
| <div | |||
| className={cn( | |||
| 'flex items-center p-2 h-12 text-xs font-medium text-gray-700 justify-between', | |||
| formattedVersion === selectedVersion ? '' : 'hover:bg-gray-100', | |||
| formattedVersion === WorkflowVersion.Draft ? 'cursor-not-allowed' : 'cursor-pointer', | |||
| )} | |||
| onClick={() => item.version !== WorkflowVersion.Draft && onClick(item)} | |||
| > | |||
| <div className='flex flex-col gap-1 py-2'> | |||
| <span className="text-left">{formatTime(formattedVersion === WorkflowVersion.Draft ? item.updated_at : item.created_at)}</span> | |||
| <span className="text-left">{t('workflow.panel.createdBy')} {item.created_by.name}</span> | |||
| </div> | |||
| {renderVersionLabel(formattedVersion)} | |||
| </div> | |||
| ) | |||
| } | |||
| export default React.memo(VersionHistoryItem) | |||
| @@ -0,0 +1,89 @@ | |||
| 'use client' | |||
| import React, { useState } from 'react' | |||
| import { useTranslation } from 'react-i18next' | |||
| import useSWR from 'swr' | |||
| import { useWorkflowRun } from '../hooks' | |||
| import VersionHistoryItem from './version-history-item' | |||
| import type { VersionHistory } from '@/types/workflow' | |||
| import { useStore as useAppStore } from '@/app/components/app/store' | |||
| import { fetchPublishedAllWorkflow } from '@/service/workflow' | |||
| import Loading from '@/app/components/base/loading' | |||
| import Button from '@/app/components/base/button' | |||
| const limit = 10 | |||
| const VersionHistoryModal = () => { | |||
| const [selectedVersion, setSelectedVersion] = useState('draft') | |||
| const [page, setPage] = useState(1) | |||
| const { handleRestoreFromPublishedWorkflow } = useWorkflowRun() | |||
| const appDetail = useAppStore.getState().appDetail | |||
| const { t } = useTranslation() | |||
| const { | |||
| data: versionHistory, | |||
| isLoading, | |||
| } = useSWR( | |||
| `/apps/${appDetail?.id}/workflows?page=${page}&limit=${limit}`, | |||
| fetchPublishedAllWorkflow, | |||
| ) | |||
| const handleVersionClick = (item: VersionHistory) => { | |||
| if (item.version !== selectedVersion) { | |||
| setSelectedVersion(item.version) | |||
| handleRestoreFromPublishedWorkflow(item) | |||
| } | |||
| } | |||
| const handleNextPage = () => { | |||
| if (versionHistory?.has_more) | |||
| setPage(page => page + 1) | |||
| } | |||
| return ( | |||
| <div className='w-[336px] bg-white rounded-2xl border-[0.5px] border-gray-200 shadow-xl p-2'> | |||
| <div className="max-h-[400px] overflow-auto"> | |||
| {(isLoading && page) === 1 | |||
| ? ( | |||
| <div className='flex items-center justify-center h-10'> | |||
| <Loading/> | |||
| </div> | |||
| ) | |||
| : ( | |||
| <> | |||
| {versionHistory?.items?.map((item, idx) => ( | |||
| <VersionHistoryItem | |||
| key={item.version} | |||
| item={item} | |||
| selectedVersion={selectedVersion} | |||
| onClick={handleVersionClick} | |||
| curIdx={idx} | |||
| page={page} | |||
| /> | |||
| ))} | |||
| {isLoading && page > 1 && ( | |||
| <div className='flex items-center justify-center h-10'> | |||
| <Loading/> | |||
| </div> | |||
| )} | |||
| {!isLoading && versionHistory?.has_more && ( | |||
| <div className='flex items-center justify-center h-10 mt-2'> | |||
| <Button | |||
| className='text-sm' | |||
| onClick={handleNextPage} | |||
| > | |||
| {t('workflow.common.loadMore')} | |||
| </Button> | |||
| </div> | |||
| )} | |||
| {!isLoading && !versionHistory?.items?.length && ( | |||
| <div className='flex items-center justify-center h-10 text-gray-500'> | |||
| {t('workflow.common.noHistory')} | |||
| </div> | |||
| )} | |||
| </> | |||
| )} | |||
| </div> | |||
| </div> | |||
| ) | |||
| } | |||
| export default React.memo(VersionHistoryModal) | |||
| @@ -202,7 +202,7 @@ const ViewHistory = ({ | |||
| {`Test ${isChatMode ? 'Chat' : 'Run'}#${item.sequence_number}`} | |||
| </div> | |||
| <div className='flex items-center text-xs text-gray-500 leading-[18px]'> | |||
| {item.created_by_account.name} · {formatTimeFromNow((item.finished_at || item.created_at) * 1000)} | |||
| {item.created_by_account?.name} · {formatTimeFromNow((item.finished_at || item.created_at) * 1000)} | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -18,17 +18,14 @@ import { useWorkflowUpdate } from './use-workflow-interactions' | |||
| import { useStore as useAppStore } from '@/app/components/app/store' | |||
| import type { IOtherOptions } from '@/service/base' | |||
| import { ssePost } from '@/service/base' | |||
| import { | |||
| fetchPublishedWorkflow, | |||
| stopWorkflowRun, | |||
| } from '@/service/workflow' | |||
| import { stopWorkflowRun } from '@/service/workflow' | |||
| import { useFeaturesStore } from '@/app/components/base/features/hooks' | |||
| import { AudioPlayerManager } from '@/app/components/base/audio-btn/audio.player.manager' | |||
| import { | |||
| getFilesInLogs, | |||
| } from '@/app/components/base/file-uploader/utils' | |||
| import { ErrorHandleTypeEnum } from '@/app/components/workflow/nodes/_base/components/error-handle/types' | |||
| import type { NodeTracing } from '@/types/workflow' | |||
| import type { NodeTracing, VersionHistory } from '@/types/workflow' | |||
| export const useWorkflowRun = () => { | |||
| const store = useStoreApi() | |||
| @@ -754,24 +751,18 @@ export const useWorkflowRun = () => { | |||
| stopWorkflowRun(`/apps/${appId}/workflow-runs/tasks/${taskId}/stop`) | |||
| }, []) | |||
| const handleRestoreFromPublishedWorkflow = useCallback(async () => { | |||
| const appDetail = useAppStore.getState().appDetail | |||
| const publishedWorkflow = await fetchPublishedWorkflow(`/apps/${appDetail?.id}/workflows/publish`) | |||
| if (publishedWorkflow) { | |||
| const nodes = publishedWorkflow.graph.nodes | |||
| const edges = publishedWorkflow.graph.edges | |||
| const viewport = publishedWorkflow.graph.viewport! | |||
| handleUpdateWorkflowCanvas({ | |||
| nodes, | |||
| edges, | |||
| viewport, | |||
| }) | |||
| featuresStore?.setState({ features: publishedWorkflow.features }) | |||
| workflowStore.getState().setPublishedAt(publishedWorkflow.created_at) | |||
| workflowStore.getState().setEnvironmentVariables(publishedWorkflow.environment_variables || []) | |||
| } | |||
| const handleRestoreFromPublishedWorkflow = useCallback((publishedWorkflow: VersionHistory) => { | |||
| const nodes = publishedWorkflow.graph.nodes.map(node => ({ ...node, selected: false, data: { ...node.data, selected: false } })) | |||
| const edges = publishedWorkflow.graph.edges | |||
| const viewport = publishedWorkflow.graph.viewport! | |||
| handleUpdateWorkflowCanvas({ | |||
| nodes, | |||
| edges, | |||
| viewport, | |||
| }) | |||
| featuresStore?.setState({ features: publishedWorkflow.features }) | |||
| workflowStore.getState().setPublishedAt(publishedWorkflow.created_at) | |||
| workflowStore.getState().setEnvironmentVariables(publishedWorkflow.environment_variables || []) | |||
| }, [featuresStore, handleUpdateWorkflowCanvas, workflowStore]) | |||
| return { | |||
| @@ -21,7 +21,7 @@ import type { | |||
| WorkflowRunningData, | |||
| } from './types' | |||
| import { WorkflowContext } from './context' | |||
| import type { NodeTracing } from '@/types/workflow' | |||
| import type { NodeTracing, VersionHistory } from '@/types/workflow' | |||
| // #TODO chatVar# | |||
| // const MOCK_DATA = [ | |||
| @@ -171,6 +171,8 @@ type Shape = { | |||
| setIterTimes: (iterTimes: number) => void | |||
| iterParallelLogMap: Map<string, Map<string, NodeTracing[]>> | |||
| setIterParallelLogMap: (iterParallelLogMap: Map<string, Map<string, NodeTracing[]>>) => void | |||
| versionHistory: VersionHistory[] | |||
| setVersionHistory: (versionHistory: VersionHistory[]) => void | |||
| } | |||
| export const createWorkflowStore = () => { | |||
| @@ -291,6 +293,8 @@ export const createWorkflowStore = () => { | |||
| iterParallelLogMap: new Map<string, Map<string, NodeTracing[]>>(), | |||
| setIterParallelLogMap: iterParallelLogMap => set(() => ({ iterParallelLogMap })), | |||
| versionHistory: [], | |||
| setVersionHistory: versionHistory => set(() => ({ versionHistory })), | |||
| })) | |||
| } | |||
| @@ -289,6 +289,11 @@ export enum WorkflowRunningStatus { | |||
| Stopped = 'stopped', | |||
| } | |||
| export enum WorkflowVersion { | |||
| Draft = 'draft', | |||
| Latest = 'latest', | |||
| } | |||
| export enum NodeRunningStatus { | |||
| NotStart = 'not-start', | |||
| Waiting = 'waiting', | |||
| @@ -104,6 +104,8 @@ const translation = { | |||
| onFailure: '异常时', | |||
| addFailureBranch: '添加异常分支', | |||
| openInExplore: '在“探索”中打开', | |||
| loadMore: '加载更多', | |||
| noHistory: '没有历史版本', | |||
| }, | |||
| env: { | |||
| envPanelTitle: '环境变量', | |||
| @@ -4,6 +4,7 @@ import type { CommonResponse } from '@/models/common' | |||
| import type { | |||
| ChatRunHistoryResponse, | |||
| ConversationVariableResponse, | |||
| FetchWorkflowDraftPageResponse, | |||
| FetchWorkflowDraftResponse, | |||
| NodesDefaultConfigsResponse, | |||
| WorkflowRunHistoryResponse, | |||
| @@ -14,7 +15,10 @@ export const fetchWorkflowDraft = (url: string) => { | |||
| return get(url, {}, { silent: true }) as Promise<FetchWorkflowDraftResponse> | |||
| } | |||
| export const syncWorkflowDraft = ({ url, params }: { url: string; params: Pick<FetchWorkflowDraftResponse, 'graph' | 'features' | 'environment_variables' | 'conversation_variables'> }) => { | |||
| export const syncWorkflowDraft = ({ url, params }: { | |||
| url: string | |||
| params: Pick<FetchWorkflowDraftResponse, 'graph' | 'features' | 'environment_variables' | 'conversation_variables'> | |||
| }) => { | |||
| return post<CommonResponse & { updated_at: number; hash: string }>(url, { body: params }, { silent: true }) | |||
| } | |||
| @@ -46,6 +50,10 @@ export const fetchPublishedWorkflow: Fetcher<FetchWorkflowDraftResponse, string> | |||
| return get<FetchWorkflowDraftResponse>(url) | |||
| } | |||
| export const fetchPublishedAllWorkflow: Fetcher<FetchWorkflowDraftPageResponse, string> = (url) => { | |||
| return get<FetchWorkflowDraftPageResponse>(url) | |||
| } | |||
| export const stopWorkflowRun = (url: string) => { | |||
| return post<CommonResponse>(url) | |||
| } | |||
| @@ -61,6 +69,9 @@ export const updateWorkflowDraftFromDSL = (appId: string, data: string) => { | |||
| return post<FetchWorkflowDraftResponse>(`apps/${appId}/workflows/draft/import`, { body: { data } }) | |||
| } | |||
| export const fetchCurrentValueOfConversationVariable: Fetcher<ConversationVariableResponse, { url: string; params: { conversation_id: string } }> = ({ url, params }) => { | |||
| export const fetchCurrentValueOfConversationVariable: Fetcher<ConversationVariableResponse, { | |||
| url: string | |||
| params: { conversation_id: string } | |||
| }> = ({ url, params }) => { | |||
| return get<ConversationVariableResponse>(url, { params }) | |||
| } | |||
| @@ -1,11 +1,5 @@ | |||
| import type { Viewport } from 'reactflow' | |||
| import type { | |||
| BlockEnum, | |||
| ConversationVariable, | |||
| Edge, | |||
| EnvironmentVariable, | |||
| Node, | |||
| } from '@/app/components/workflow/types' | |||
| import type { BlockEnum, ConversationVariable, Edge, EnvironmentVariable, Node } from '@/app/components/workflow/types' | |||
| import type { TransferMethod } from '@/types/app' | |||
| import type { ErrorHandleTypeEnum } from '@/app/components/workflow/nodes/_base/components/error-handle/types' | |||
| @@ -79,6 +73,15 @@ export type FetchWorkflowDraftResponse = { | |||
| tool_published: boolean | |||
| environment_variables?: EnvironmentVariable[] | |||
| conversation_variables?: ConversationVariable[] | |||
| version: string | |||
| } | |||
| export type VersionHistory = FetchWorkflowDraftResponse | |||
| export type FetchWorkflowDraftPageResponse = { | |||
| items: VersionHistory[] | |||
| has_more: boolean | |||
| page: number | |||
| } | |||
| export type NodeTracingListResponse = { | |||
| @@ -1,11 +1,16 @@ | |||
| import { MAX_VAR_KEY_LENGTH, VAR_ITEM_TEMPLATE, VAR_ITEM_TEMPLATE_IN_WORKFLOW, getMaxVarNameLength } from '@/config' | |||
| import { CONTEXT_PLACEHOLDER_TEXT, HISTORY_PLACEHOLDER_TEXT, PRE_PROMPT_PLACEHOLDER_TEXT, QUERY_PLACEHOLDER_TEXT } from '@/app/components/base/prompt-editor/constants' | |||
| import { | |||
| CONTEXT_PLACEHOLDER_TEXT, | |||
| HISTORY_PLACEHOLDER_TEXT, | |||
| PRE_PROMPT_PLACEHOLDER_TEXT, | |||
| QUERY_PLACEHOLDER_TEXT, | |||
| } from '@/app/components/base/prompt-editor/constants' | |||
| import { InputVarType } from '@/app/components/workflow/types' | |||
| const otherAllowedRegex = /^[a-zA-Z0-9_]+$/ | |||
| export const getNewVar = (key: string, type: string) => { | |||
| const { max_length, ...rest } = VAR_ITEM_TEMPLATE | |||
| const { ...rest } = VAR_ITEM_TEMPLATE | |||
| if (type !== 'string') { | |||
| return { | |||
| ...rest, | |||