From d5fc3e7bed4f3d8853cc9e4f18347842aa77371b Mon Sep 17 00:00:00 2001 From: hjlarry Date: Wed, 10 Sep 2025 09:24:22 +0800 Subject: [PATCH] add new conversation vars update api --- .../console/app/workflow_draft_variable.py | 28 +++++ api/services/workflow_service.py | 22 ++++ .../panel/chat-variable-panel/index.tsx | 111 +++++++++++++----- web/service/workflow.ts | 11 +- 4 files changed, 142 insertions(+), 30 deletions(-) diff --git a/api/controllers/console/app/workflow_draft_variable.py b/api/controllers/console/app/workflow_draft_variable.py index ef31a5a9bb..961c1bfcc7 100644 --- a/api/controllers/console/app/workflow_draft_variable.py +++ b/api/controllers/console/app/workflow_draft_variable.py @@ -370,6 +370,34 @@ class ConversationVariableCollectionApi(Resource): draft_var_srv.prefill_conversation_variable_default_values(draft_workflow) db.session.commit() return _get_variable_list(app_model, CONVERSATION_VARIABLE_NODE_ID) + + @setup_required + @login_required + @account_initialization_required + @get_app_model(mode=AppMode.ADVANCED_CHAT) + def post(self, app_model: App): + # The role of the current user in the ta table must be admin, owner, or editor + if not current_user.is_editor: + raise Forbidden() + + parser = reqparse.RequestParser() + parser.add_argument("conversation_variables", type=list, required=True, location="json") + args = parser.parse_args() + + workflow_service = WorkflowService() + + conversation_variables_list = args.get("conversation_variables") or [] + conversation_variables = [ + variable_factory.build_conversation_variable_from_mapping(obj) for obj in conversation_variables_list + ] + + workflow_service.update_draft_workflow_conversation_variables( + app_model=app_model, + account=current_user, + conversation_variables=conversation_variables, + ) + + return { "result": "success" } class SystemVariableCollectionApi(Resource): diff --git a/api/services/workflow_service.py b/api/services/workflow_service.py index 499b86360b..058e949a5a 100644 --- a/api/services/workflow_service.py +++ b/api/services/workflow_service.py @@ -267,6 +267,28 @@ class WorkflowService: # commit db session changes db.session.commit() + def update_draft_workflow_conversation_variables( + self, *, + app_model: App, + conversation_variables: Sequence[Variable], + account: Account, + ): + """ + Update draft workflow conversation variables + """ + # fetch draft workflow by app_model + workflow = self.get_draft_workflow(app_model=app_model) + + if not workflow: + raise ValueError("No draft workflow found.") + + workflow.conversation_variables = conversation_variables + 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/panel/chat-variable-panel/index.tsx b/web/app/components/workflow/panel/chat-variable-panel/index.tsx index e87db1eafe..090dd077fd 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/index.tsx +++ b/web/app/components/workflow/panel/chat-variable-panel/index.tsx @@ -19,12 +19,12 @@ import type { ConversationVariable, } from '@/app/components/workflow/types' import { findUsedVarNodes, updateNodeVars } from '@/app/components/workflow/nodes/_base/components/variable/utils' -import { useNodesSyncDraft } from '@/app/components/workflow/hooks/use-nodes-sync-draft' import { BlockEnum } from '@/app/components/workflow/types' import { webSocketClient } from '@/app/components/workflow/collaboration/core/websocket-manager' import { useDocLink } from '@/context/i18n' import cn from '@/utils/classnames' import useInspectVarsCrud from '../../hooks/use-inspect-vars-crud' +import { updateConversationVariables } from '@/service/workflow' const ChatVariablePanel = () => { const { t } = useTranslation() @@ -34,25 +34,9 @@ const ChatVariablePanel = () => { const varList = useStore(s => s.conversationVariables) as ConversationVariable[] const updateChatVarList = useStore(s => s.setConversationVariables) const appId = useStore(s => s.appId) - const { doSyncWorkflowDraft } = useNodesSyncDraft() const { invalidateConversationVarValues, } = useInspectVarsCrud() - const handleVarChanged = useCallback(() => { - doSyncWorkflowDraft(false, { - onSuccess() { - invalidateConversationVarValues() - if (appId) { - const socket = webSocketClient.getSocket(appId) - if (socket) { - socket.emit('collaboration_event', { - type: 'varsAndFeaturesUpdate', - }) - } - } - }, - }) - }, [doSyncWorkflowDraft, invalidateConversationVarValues, appId]) const [showTip, setShowTip] = useState(true) const [showVariableModal, setShowVariableModal] = useState(false) @@ -87,13 +71,36 @@ const ChatVariablePanel = () => { setShowVariableModal(true) } - const handleDelete = useCallback((chatVar: ConversationVariable) => { + const handleDelete = useCallback(async (chatVar: ConversationVariable) => { removeUsedVarInNodes(chatVar) - updateChatVarList(varList.filter(v => v.id !== chatVar.id)) + const newVarList = varList.filter(v => v.id !== chatVar.id) + updateChatVarList(newVarList) setCacheForDelete(undefined) setShowRemoveConfirm(false) - handleVarChanged() - }, [handleVarChanged, removeUsedVarInNodes, updateChatVarList, varList]) + + // Use new dedicated conversation variables API instead of workflow draft sync + try { + await updateConversationVariables({ + appId, + conversationVariables: newVarList, + }) + + // Emit update event to other connected clients + const socket = webSocketClient.getSocket(appId) + if (socket) { + socket.emit('collaboration_event', { + type: 'varsAndFeaturesUpdate', + }) + } + + invalidateConversationVarValues() + } + catch (error) { + console.error('Failed to update conversation variables:', error) + // Revert local state on error + updateChatVarList(varList) + } + }, [removeUsedVarInNodes, updateChatVarList, varList, appId, invalidateConversationVarValues]) const deleteCheck = useCallback((chatVar: ConversationVariable) => { const effectedNodes = getEffectedNodes(chatVar) @@ -107,17 +114,42 @@ const ChatVariablePanel = () => { }, [getEffectedNodes, handleDelete]) const handleSave = useCallback(async (chatVar: ConversationVariable) => { - // add chatVar + let newList: ConversationVariable[] + if (!currentVar) { - const newList = [chatVar, ...varList] + // Adding new conversation variable + newList = [chatVar, ...varList] updateChatVarList(newList) - handleVarChanged() + + // Use new dedicated conversation variables API + try { + await updateConversationVariables({ + appId, + conversationVariables: newList, + }) + + const socket = webSocketClient.getSocket(appId) + if (socket) { + socket.emit('collaboration_event', { + type: 'varsAndFeaturesUpdate', + }) + } + + invalidateConversationVarValues() + } + catch (error) { + console.error('Failed to update conversation variables:', error) + // Revert local state on error + updateChatVarList(varList) + } return } - // edit chatVar - const newList = varList.map(v => v.id === currentVar.id ? chatVar : v) + + // Updating existing conversation variable + newList = varList.map(v => v.id === currentVar.id ? chatVar : v) updateChatVarList(newList) - // side effects of rename env + + // side effects of rename conversation variable if (currentVar.name !== chatVar.name) { const { getNodes, setNodes } = store.getState() const effectedNodes = getEffectedNodes(currentVar) @@ -129,8 +161,29 @@ const ChatVariablePanel = () => { }) setNodes(newNodes) } - handleVarChanged() - }, [currentVar, getEffectedNodes, handleVarChanged, store, updateChatVarList, varList]) + + // Use new dedicated conversation variables API + try { + await updateConversationVariables({ + appId, + conversationVariables: newList, + }) + + const socket = webSocketClient.getSocket(appId) + if (socket) { + socket.emit('collaboration_event', { + type: 'varsAndFeaturesUpdate', + }) + } + + invalidateConversationVarValues() + } + catch (error) { + console.error('Failed to update conversation variables:', error) + // Revert local state on error + updateChatVarList(varList) + } + }, [currentVar, getEffectedNodes, store, updateChatVarList, varList, appId, invalidateConversationVarValues]) return (
{ return get(url, {}, { silent: true }) as Promise @@ -115,3 +115,12 @@ export const updateEnvironmentVariables = ({ appId, environmentVariables }: { body: { environment_variables: environmentVariables }, }) } + +export const updateConversationVariables = ({ appId, conversationVariables }: { + appId: string + conversationVariables: ConversationVariable[] +}) => { + return post(`apps/${appId}/workflows/draft/conversation-variables`, { + body: { conversation_variables: conversationVariables }, + }) +}