Co-authored-by: hobo.l <hobo.l@binance.com> Co-authored-by: crazywoola <427733928@qq.com>tags/0.15.0
| import logging | import logging | ||||
| from flask import abort, request | 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 | from werkzeug.exceptions import Forbidden, InternalServerError, NotFound | ||||
| import services | import services | ||||
| from core.app.apps.base_app_queue_manager import AppQueueManager | from core.app.apps.base_app_queue_manager import AppQueueManager | ||||
| from core.app.entities.app_invoke_entities import InvokeFrom | from core.app.entities.app_invoke_entities import InvokeFrom | ||||
| from factories import variable_factory | 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 fields.workflow_run_fields import workflow_run_node_execution_fields | ||||
| from libs import helper | from libs import helper | ||||
| from libs.helper import TimestampField, uuid_value | from libs.helper import TimestampField, uuid_value | ||||
| } | } | ||||
| 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(DraftWorkflowApi, "/apps/<uuid:app_id>/workflows/draft") | ||||
| api.add_resource(WorkflowConfigApi, "/apps/<uuid:app_id>/workflows/draft/config") | api.add_resource(WorkflowConfigApi, "/apps/<uuid:app_id>/workflows/draft/config") | ||||
| api.add_resource(AdvancedChatDraftWorkflowRunApi, "/apps/<uuid:app_id>/advanced-chat/workflows/draft/run") | api.add_resource(AdvancedChatDraftWorkflowRunApi, "/apps/<uuid:app_id>/advanced-chat/workflows/draft/run") | ||||
| WorkflowDraftRunIterationNodeApi, "/apps/<uuid:app_id>/workflows/draft/iteration/nodes/<string:node_id>/run" | 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(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(DefaultBlockConfigsApi, "/apps/<uuid:app_id>/workflows/default-workflow-block-configs") | ||||
| api.add_resource( | api.add_resource( | ||||
| DefaultBlockConfigApi, "/apps/<uuid:app_id>/workflows/default-workflow-block-configs/<string:block_type>" | DefaultBlockConfigApi, "/apps/<uuid:app_id>/workflows/default-workflow-block-configs/<string:block_type>" |
| "graph": fields.Raw(attribute="graph_dict"), | "graph": fields.Raw(attribute="graph_dict"), | ||||
| "features": fields.Raw(attribute="features_dict"), | "features": fields.Raw(attribute="features_dict"), | ||||
| "hash": fields.String(attribute="unique_hash"), | "hash": fields.String(attribute="unique_hash"), | ||||
| "version": fields.String(attribute="version"), | |||||
| "created_by": fields.Nested(simple_account_fields, attribute="created_by_account"), | "created_by": fields.Nested(simple_account_fields, attribute="created_by_account"), | ||||
| "created_at": TimestampField, | "created_at": TimestampField, | ||||
| "updated_by": fields.Nested(simple_account_fields, attribute="updated_by_account", allow_null=True), | "updated_by": fields.Nested(simple_account_fields, attribute="updated_by_account", allow_null=True), | ||||
| "updated_by": fields.String, | "updated_by": fields.String, | ||||
| "updated_at": TimestampField, | "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"), | |||||
| } |
| from typing import Any, Optional, cast | from typing import Any, Optional, cast | ||||
| from uuid import uuid4 | from uuid import uuid4 | ||||
| from sqlalchemy import desc | |||||
| from core.app.apps.advanced_chat.app_config_manager import AdvancedChatAppConfigManager | from core.app.apps.advanced_chat.app_config_manager import AdvancedChatAppConfigManager | ||||
| from core.app.apps.workflow.app_config_manager import WorkflowAppConfigManager | from core.app.apps.workflow.app_config_manager import WorkflowAppConfigManager | ||||
| from core.model_runtime.utils.encoders import jsonable_encoder | from core.model_runtime.utils.encoders import jsonable_encoder | ||||
| return workflow | 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( | def sync_draft_workflow( | ||||
| self, | self, | ||||
| *, | *, |
| import { | import { | ||||
| useChecklistBeforePublish, | useChecklistBeforePublish, | ||||
| useIsChatMode, | useIsChatMode, | ||||
| useNodesInteractions, | |||||
| useNodesReadOnly, | useNodesReadOnly, | ||||
| useNodesSyncDraft, | useNodesSyncDraft, | ||||
| useWorkflowMode, | useWorkflowMode, | ||||
| import ViewHistory from './view-history' | import ViewHistory from './view-history' | ||||
| import ChatVariableButton from './chat-variable-button' | import ChatVariableButton from './chat-variable-button' | ||||
| import EnvButton from './env-button' | import EnvButton from './env-button' | ||||
| import VersionHistoryModal from './version-history-modal' | |||||
| import Button from '@/app/components/base/button' | import Button from '@/app/components/base/button' | ||||
| import { useStore as useAppStore } from '@/app/components/app/store' | import { useStore as useAppStore } from '@/app/components/app/store' | ||||
| import { publishWorkflow } from '@/service/workflow' | import { publishWorkflow } from '@/service/workflow' | ||||
| const appID = appDetail?.id | const appID = appDetail?.id | ||||
| const isChatMode = useIsChatMode() | const isChatMode = useIsChatMode() | ||||
| const { nodesReadOnly, getNodesReadOnly } = useNodesReadOnly() | const { nodesReadOnly, getNodesReadOnly } = useNodesReadOnly() | ||||
| const { handleNodeSelect } = useNodesInteractions() | |||||
| const publishedAt = useStore(s => s.publishedAt) | const publishedAt = useStore(s => s.publishedAt) | ||||
| const draftUpdatedAt = useStore(s => s.draftUpdatedAt) | const draftUpdatedAt = useStore(s => s.draftUpdatedAt) | ||||
| const toolPublished = useStore(s => s.toolPublished) | const toolPublished = useStore(s => s.toolPublished) | ||||
| const nodes = useNodes<StartNodeType>() | const nodes = useNodes<StartNodeType>() | ||||
| const startNode = nodes.find(node => node.data.type === BlockEnum.Start) | const startNode = nodes.find(node => node.data.type === BlockEnum.Start) | ||||
| const selectedNode = nodes.find(node => node.data.selected) | |||||
| const startVariables = startNode?.data.variables | const startVariables = startNode?.data.variables | ||||
| const fileSettings = useFeatures(s => s.features.file) | const fileSettings = useFeatures(s => s.features.file) | ||||
| const variables = useMemo(() => { | const variables = useMemo(() => { | ||||
| const { | const { | ||||
| handleLoadBackupDraft, | handleLoadBackupDraft, | ||||
| handleBackupDraft, | handleBackupDraft, | ||||
| handleRestoreFromPublishedWorkflow, | |||||
| } = useWorkflowRun() | } = useWorkflowRun() | ||||
| const { handleCheckBeforePublish } = useChecklistBeforePublish() | const { handleCheckBeforePublish } = useChecklistBeforePublish() | ||||
| const { handleSyncWorkflowDraft } = useNodesSyncDraft() | const { handleSyncWorkflowDraft } = useNodesSyncDraft() | ||||
| const onStartRestoring = useCallback(() => { | const onStartRestoring = useCallback(() => { | ||||
| workflowStore.setState({ isRestoring: true }) | workflowStore.setState({ isRestoring: true }) | ||||
| handleBackupDraft() | handleBackupDraft() | ||||
| handleRestoreFromPublishedWorkflow() | |||||
| }, [handleBackupDraft, handleRestoreFromPublishedWorkflow, workflowStore]) | |||||
| // clear right panel | |||||
| if (selectedNode) | |||||
| handleNodeSelect(selectedNode.id, true) | |||||
| }, [handleBackupDraft, workflowStore, handleNodeSelect, selectedNode]) | |||||
| const onPublisherToggle = useCallback((state: boolean) => { | const onPublisherToggle = useCallback((state: boolean) => { | ||||
| if (state) | if (state) | ||||
| } | } | ||||
| { | { | ||||
| restoring && ( | 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> | </div> | ||||
| ) | ) | ||||
| } | } |
| 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) |
| '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) |
| {`Test ${isChatMode ? 'Chat' : 'Run'}#${item.sequence_number}`} | {`Test ${isChatMode ? 'Chat' : 'Run'}#${item.sequence_number}`} | ||||
| </div> | </div> | ||||
| <div className='flex items-center text-xs text-gray-500 leading-[18px]'> | <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> | </div> | ||||
| </div> | </div> |
| import { useStore as useAppStore } from '@/app/components/app/store' | import { useStore as useAppStore } from '@/app/components/app/store' | ||||
| import type { IOtherOptions } from '@/service/base' | import type { IOtherOptions } from '@/service/base' | ||||
| import { ssePost } 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 { useFeaturesStore } from '@/app/components/base/features/hooks' | ||||
| import { AudioPlayerManager } from '@/app/components/base/audio-btn/audio.player.manager' | import { AudioPlayerManager } from '@/app/components/base/audio-btn/audio.player.manager' | ||||
| import { | import { | ||||
| getFilesInLogs, | getFilesInLogs, | ||||
| } from '@/app/components/base/file-uploader/utils' | } from '@/app/components/base/file-uploader/utils' | ||||
| import { ErrorHandleTypeEnum } from '@/app/components/workflow/nodes/_base/components/error-handle/types' | 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 = () => { | export const useWorkflowRun = () => { | ||||
| const store = useStoreApi() | const store = useStoreApi() | ||||
| stopWorkflowRun(`/apps/${appId}/workflow-runs/tasks/${taskId}/stop`) | 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]) | }, [featuresStore, handleUpdateWorkflowCanvas, workflowStore]) | ||||
| return { | return { |
| WorkflowRunningData, | WorkflowRunningData, | ||||
| } from './types' | } from './types' | ||||
| import { WorkflowContext } from './context' | import { WorkflowContext } from './context' | ||||
| import type { NodeTracing } from '@/types/workflow' | |||||
| import type { NodeTracing, VersionHistory } from '@/types/workflow' | |||||
| // #TODO chatVar# | // #TODO chatVar# | ||||
| // const MOCK_DATA = [ | // const MOCK_DATA = [ | ||||
| setIterTimes: (iterTimes: number) => void | setIterTimes: (iterTimes: number) => void | ||||
| iterParallelLogMap: Map<string, Map<string, NodeTracing[]>> | iterParallelLogMap: Map<string, Map<string, NodeTracing[]>> | ||||
| setIterParallelLogMap: (iterParallelLogMap: Map<string, Map<string, NodeTracing[]>>) => void | setIterParallelLogMap: (iterParallelLogMap: Map<string, Map<string, NodeTracing[]>>) => void | ||||
| versionHistory: VersionHistory[] | |||||
| setVersionHistory: (versionHistory: VersionHistory[]) => void | |||||
| } | } | ||||
| export const createWorkflowStore = () => { | export const createWorkflowStore = () => { | ||||
| iterParallelLogMap: new Map<string, Map<string, NodeTracing[]>>(), | iterParallelLogMap: new Map<string, Map<string, NodeTracing[]>>(), | ||||
| setIterParallelLogMap: iterParallelLogMap => set(() => ({ iterParallelLogMap })), | setIterParallelLogMap: iterParallelLogMap => set(() => ({ iterParallelLogMap })), | ||||
| versionHistory: [], | |||||
| setVersionHistory: versionHistory => set(() => ({ versionHistory })), | |||||
| })) | })) | ||||
| } | } | ||||
| Stopped = 'stopped', | Stopped = 'stopped', | ||||
| } | } | ||||
| export enum WorkflowVersion { | |||||
| Draft = 'draft', | |||||
| Latest = 'latest', | |||||
| } | |||||
| export enum NodeRunningStatus { | export enum NodeRunningStatus { | ||||
| NotStart = 'not-start', | NotStart = 'not-start', | ||||
| Waiting = 'waiting', | Waiting = 'waiting', |
| onFailure: '异常时', | onFailure: '异常时', | ||||
| addFailureBranch: '添加异常分支', | addFailureBranch: '添加异常分支', | ||||
| openInExplore: '在“探索”中打开', | openInExplore: '在“探索”中打开', | ||||
| loadMore: '加载更多', | |||||
| noHistory: '没有历史版本', | |||||
| }, | }, | ||||
| env: { | env: { | ||||
| envPanelTitle: '环境变量', | envPanelTitle: '环境变量', |
| import type { | import type { | ||||
| ChatRunHistoryResponse, | ChatRunHistoryResponse, | ||||
| ConversationVariableResponse, | ConversationVariableResponse, | ||||
| FetchWorkflowDraftPageResponse, | |||||
| FetchWorkflowDraftResponse, | FetchWorkflowDraftResponse, | ||||
| NodesDefaultConfigsResponse, | NodesDefaultConfigsResponse, | ||||
| WorkflowRunHistoryResponse, | WorkflowRunHistoryResponse, | ||||
| return get(url, {}, { silent: true }) as Promise<FetchWorkflowDraftResponse> | 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 }) | return post<CommonResponse & { updated_at: number; hash: string }>(url, { body: params }, { silent: true }) | ||||
| } | } | ||||
| return get<FetchWorkflowDraftResponse>(url) | return get<FetchWorkflowDraftResponse>(url) | ||||
| } | } | ||||
| export const fetchPublishedAllWorkflow: Fetcher<FetchWorkflowDraftPageResponse, string> = (url) => { | |||||
| return get<FetchWorkflowDraftPageResponse>(url) | |||||
| } | |||||
| export const stopWorkflowRun = (url: string) => { | export const stopWorkflowRun = (url: string) => { | ||||
| return post<CommonResponse>(url) | return post<CommonResponse>(url) | ||||
| } | } | ||||
| return post<FetchWorkflowDraftResponse>(`apps/${appId}/workflows/draft/import`, { body: { data } }) | 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 }) | return get<ConversationVariableResponse>(url, { params }) | ||||
| } | } |
| import type { Viewport } from 'reactflow' | 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 { TransferMethod } from '@/types/app' | ||||
| import type { ErrorHandleTypeEnum } from '@/app/components/workflow/nodes/_base/components/error-handle/types' | import type { ErrorHandleTypeEnum } from '@/app/components/workflow/nodes/_base/components/error-handle/types' | ||||
| tool_published: boolean | tool_published: boolean | ||||
| environment_variables?: EnvironmentVariable[] | environment_variables?: EnvironmentVariable[] | ||||
| conversation_variables?: ConversationVariable[] | conversation_variables?: ConversationVariable[] | ||||
| version: string | |||||
| } | |||||
| export type VersionHistory = FetchWorkflowDraftResponse | |||||
| export type FetchWorkflowDraftPageResponse = { | |||||
| items: VersionHistory[] | |||||
| has_more: boolean | |||||
| page: number | |||||
| } | } | ||||
| export type NodeTracingListResponse = { | export type NodeTracingListResponse = { |
| import { MAX_VAR_KEY_LENGTH, VAR_ITEM_TEMPLATE, VAR_ITEM_TEMPLATE_IN_WORKFLOW, getMaxVarNameLength } from '@/config' | 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' | import { InputVarType } from '@/app/components/workflow/types' | ||||
| const otherAllowedRegex = /^[a-zA-Z0-9_]+$/ | const otherAllowedRegex = /^[a-zA-Z0-9_]+$/ | ||||
| export const getNewVar = (key: string, type: string) => { | export const getNewVar = (key: string, type: string) => { | ||||
| const { max_length, ...rest } = VAR_ITEM_TEMPLATE | |||||
| const { ...rest } = VAR_ITEM_TEMPLATE | |||||
| if (type !== 'string') { | if (type !== 'string') { | ||||
| return { | return { | ||||
| ...rest, | ...rest, |