From 21fee59b22462e6b051088b222c1d7abbd5700a1 Mon Sep 17 00:00:00 2001 From: hjlarry Date: Wed, 10 Sep 2025 14:24:38 +0800 Subject: [PATCH] use new features update api --- api/controllers/console/app/workflow.py | 25 +++++++++++ api/models/workflow.py | 2 +- api/services/workflow_service.py | 25 +++++++++++ web/app/components/workflow/features.tsx | 57 +++++++++++++++++------- web/service/workflow.ts | 10 +++++ 5 files changed, 102 insertions(+), 17 deletions(-) diff --git a/api/controllers/console/app/workflow.py b/api/controllers/console/app/workflow.py index e24deeb14e..f67c629d48 100644 --- a/api/controllers/console/app/workflow.py +++ b/api/controllers/console/app/workflow.py @@ -628,6 +628,27 @@ class WorkflowConfigApi(Resource): } +class WorkflowFeaturesApi(Resource): + """Update draft workflow features.""" + + @setup_required + @login_required + @account_initialization_required + @get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW]) + def post(self, app_model: App): + parser = reqparse.RequestParser() + parser.add_argument("features", type=dict, required=True, location="json") + args = parser.parse_args() + + features = args.get("features") + + # Update draft workflow features + workflow_service = WorkflowService() + workflow_service.update_draft_workflow_features(app_model=app_model, features=features, account=current_user) + + return {"result": "success"} + + class PublishedAllWorkflowApi(Resource): @setup_required @login_required @@ -826,6 +847,10 @@ api.add_resource( WorkflowConfigApi, "/apps//workflows/draft/config", ) +api.add_resource( + WorkflowFeaturesApi, + "/apps//workflows/draft/features", +) api.add_resource( AdvancedChatDraftWorkflowRunApi, "/apps//advanced-chat/workflows/draft/run", diff --git a/api/models/workflow.py b/api/models/workflow.py index 9930859201..a828a05845 100644 --- a/api/models/workflow.py +++ b/api/models/workflow.py @@ -325,7 +325,7 @@ class Workflow(Base): :return: hash """ - entity = {"graph": self.graph_dict, "features": self.features_dict} + entity = {"graph": self.graph_dict} return helper.generate_text_hash(json.dumps(entity, sort_keys=True)) diff --git a/api/services/workflow_service.py b/api/services/workflow_service.py index 058e949a5a..e40fd68429 100644 --- a/api/services/workflow_service.py +++ b/api/services/workflow_service.py @@ -289,6 +289,31 @@ class WorkflowService: # commit db session changes db.session.commit() + def update_draft_workflow_features( + self, *, + app_model: App, + features: dict, + account: Account, + ): + """ + Update draft workflow features + """ + # fetch draft workflow by app_model + workflow = self.get_draft_workflow(app_model=app_model) + + if not workflow: + raise ValueError("No draft workflow found.") + + # validate features structure + self.validate_features_structure(app_model=app_model, features=features) + + workflow.features = json.dumps(features) + workflow.updated_by = account.id + workflow.updated_at = datetime.now(UTC).replace(tzinfo=None) + + # commit db session changes + db.session.commit() + def publish_workflow( self, *, diff --git a/web/app/components/workflow/features.tsx b/web/app/components/workflow/features.tsx index f712fb9887..dcf2c93d2d 100644 --- a/web/app/components/workflow/features.tsx +++ b/web/app/components/workflow/features.tsx @@ -7,7 +7,6 @@ import { useStore } from './store' import { useIsChatMode, useNodesReadOnly, - useNodesSyncDraft, } from './hooks' import { type CommonNodeType, type InputVar, InputVarType, type Node } from './types' import useConfig from './nodes/start/use-config' @@ -15,13 +14,15 @@ import type { StartNodeType } from './nodes/start/types' import type { PromptVariable } from '@/models/debug' import NewFeaturePanel from '@/app/components/base/features/new-feature-panel' import { webSocketClient } from '@/app/components/workflow/collaboration/core/websocket-manager' +import { useFeaturesStore } from '@/app/components/base/features/hooks' +import { updateFeatures } from '@/service/workflow' const Features = () => { const setShowFeaturesPanel = useStore(s => s.setShowFeaturesPanel) const appId = useStore(s => s.appId) const isChatMode = useIsChatMode() const { nodesReadOnly } = useNodesReadOnly() - const { doSyncWorkflowDraft } = useNodesSyncDraft() + const featuresStore = useFeaturesStore() const nodes = useNodes() const startNode = nodes.find(node => node.data.type === 'start') const { id, data } = startNode as Node @@ -40,21 +41,45 @@ const Features = () => { handleAddVariable(startNodeVariable) } - const handleFeaturesChange = useCallback(() => { - doSyncWorkflowDraft(false, { - onSuccess() { - if (appId) { - const socket = webSocketClient.getSocket(appId) - if (socket) { - socket.emit('collaboration_event', { - type: 'varsAndFeaturesUpdate', - }) - } - } - }, - }) + const handleFeaturesChange = useCallback(async () => { + if (!appId || !featuresStore) return + + try { + const currentFeatures = featuresStore.getState().features + + // Transform features to match the expected server format (same as doSyncWorkflowDraft) + const transformedFeatures = { + opening_statement: currentFeatures.opening?.enabled ? (currentFeatures.opening?.opening_statement || '') : '', + suggested_questions: currentFeatures.opening?.enabled ? (currentFeatures.opening?.suggested_questions || []) : [], + suggested_questions_after_answer: currentFeatures.suggested, + text_to_speech: currentFeatures.text2speech, + speech_to_text: currentFeatures.speech2text, + retriever_resource: currentFeatures.citation, + sensitive_word_avoidance: currentFeatures.moderation, + file_upload: currentFeatures.file, + } + + console.log('Sending features to server:', transformedFeatures) + + await updateFeatures({ + appId, + features: transformedFeatures, + }) + + // Emit update event to other connected clients + const socket = webSocketClient.getSocket(appId) + if (socket) { + socket.emit('collaboration_event', { + type: 'varsAndFeaturesUpdate', + }) + } + } + catch (error) { + console.error('Failed to update features:', error) + } + setShowFeaturesPanel(true) - }, [doSyncWorkflowDraft, setShowFeaturesPanel, appId]) + }, [appId, featuresStore, setShowFeaturesPanel]) return ( { return get(url, {}, { silent: true }) as Promise @@ -124,3 +125,12 @@ export const updateConversationVariables = ({ appId, conversationVariables }: { body: { conversation_variables: conversationVariables }, }) } + +export const updateFeatures = ({ appId, features }: { + appId: string + features: Features +}) => { + return post(`apps/${appId}/workflows/draft/features`, { + body: { features }, + }) +}