This commit is contained in:
Joel
2026-06-17 10:08:40 +08:00
parent 6cb68a0122
commit 8bd8cd150b

View File

@ -0,0 +1,166 @@
import type { AgentSoulConfig } from '@dify/contracts/api/console/apps/types.gen'
import type { DefaultModelResponse } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { debounce } from 'es-toolkit/compat'
import { useStore as useJotaiStore, useSetAtom } from 'jotai'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useHooksStore } from '@/app/components/workflow/hooks-store'
import { useSerialAsyncCallback } from '@/app/components/workflow/hooks/use-serial-async-callback'
import { agentSoulConfigToFormState, formStateToAgentSoulConfig } from '@/features/agent-v2/agent-composer/conversions'
import {
agentComposerDraftAtom,
agentComposerOriginalConfigAtom,
agentComposerOriginalDraftAtom,
isAgentComposerDirtyAtom,
} from '@/features/agent-v2/agent-composer/store'
import { consoleQuery } from '@/service/client'
import { FlowType } from '@/types/common'
const DRAFT_AUTOSAVE_WAIT = 5000
function getModelProviderPluginId(provider: string) {
const [organization, pluginName] = provider.split('/').filter(Boolean)
if (organization && pluginName)
return `${organization}/${pluginName}`
return provider ? `langgenius/${provider}` : ''
}
export function getDefaultAgentSoul(defaultModel?: DefaultModelResponse): AgentSoulConfig | undefined {
if (!defaultModel)
return undefined
const modelProvider = defaultModel.provider.provider
return {
schema_version: 1,
prompt: {
system_prompt: '',
},
model: {
model_provider: modelProvider,
model: defaultModel.model,
plugin_id: getModelProviderPluginId(modelProvider),
},
}
}
export function useWorkflowInlineAgentConfigureSync({
nodeId,
baseConfig,
currentModel,
enabled,
}: {
nodeId: string
baseConfig?: AgentSoulConfig
currentModel?: {
provider: string
model: string
plugin_id?: string
}
enabled: boolean
}) {
const queryClient = useQueryClient()
const configsMap = useHooksStore(state => state.configsMap)
const store = useJotaiStore()
const setOriginalConfig = useSetAtom(agentComposerOriginalConfigAtom)
const setOriginalDraft = useSetAtom(agentComposerOriginalDraftAtom)
const [draftSavedAt, setDraftSavedAt] = useState<number | undefined>(undefined)
const baseConfigRef = useRef(baseConfig)
const currentModelRef = useRef(currentModel)
const enabledRef = useRef(enabled)
const lastAutosavedDraftKeyRef = useRef<string | undefined>(undefined)
const saveComposerMutation = useMutation(
consoleQuery.apps.byAppId.workflows.draft.nodes.byNodeId.agentComposer.put.mutationOptions(),
)
baseConfigRef.current = baseConfig
currentModelRef.current = currentModel
enabledRef.current = enabled
const getAgentSoulDraft = useCallback(() => formStateToAgentSoulConfig({
baseConfig: baseConfigRef.current,
formState: store.get(agentComposerDraftAtom),
currentModel: currentModelRef.current,
}), [store])
const saveComposer = useSerialAsyncCallback(async (configSnapshot: AgentSoulConfig) => {
if (!configsMap?.flowId || configsMap.flowType !== FlowType.appFlow)
return
const savedDraftKey = JSON.stringify(configSnapshot)
const composerState = await saveComposerMutation.mutateAsync({
params: {
app_id: configsMap.flowId,
node_id: nodeId,
},
body: {
variant: 'workflow',
save_strategy: 'node_job_only',
agent_soul: configSnapshot,
},
})
queryClient.setQueryData(
consoleQuery.apps.byAppId.workflows.draft.nodes.byNodeId.agentComposer.get.queryKey({
input: {
params: {
app_id: configsMap.flowId,
node_id: nodeId,
},
},
}),
composerState,
)
setOriginalConfig(composerState.agent_soul)
setOriginalDraft(agentSoulConfigToFormState(composerState.agent_soul))
setDraftSavedAt(Date.now())
lastAutosavedDraftKeyRef.current = savedDraftKey
})
const latestDraftSaveRef = useRef<() => void>(() => undefined)
latestDraftSaveRef.current = () => {
void saveComposer(getAgentSoulDraft())
}
const debouncedSaveDraft = useMemo(() => debounce(() => {
latestDraftSaveRef.current()
}, DRAFT_AUTOSAVE_WAIT), [])
const saveDraft = useCallback(async () => {
if (!enabledRef.current)
return
debouncedSaveDraft.cancel?.()
await saveComposer(getAgentSoulDraft())
}, [debouncedSaveDraft, getAgentSoulDraft, saveComposer])
useEffect(() => {
return store.sub(agentComposerDraftAtom, () => {
const agentSoulDraft = getAgentSoulDraft()
const agentSoulDraftKey = JSON.stringify(agentSoulDraft)
if (
!enabledRef.current
|| !store.get(isAgentComposerDirtyAtom)
|| lastAutosavedDraftKeyRef.current === agentSoulDraftKey
) {
return
}
debouncedSaveDraft()
})
}, [debouncedSaveDraft, getAgentSoulDraft, store])
useEffect(() => {
return () => {
debouncedSaveDraft.flush?.()
}
}, [debouncedSaveDraft])
return {
draftSavedAt,
saveDraft,
}
}