langgenius / dify

Dify is an open-source LLM app development platform. Dify's intuitive interface combines AI workflow, RAG pipeline, agent capabilities, model management, observability features and more, letting you quickly go from prototype to production.
https://dify.ai
Other
45.54k stars 6.4k forks source link

Input variables cannot be updated #7846

Open hanhaiszw opened 2 weeks ago

hanhaiszw commented 2 weeks ago

Self Checks

Dify version

0.7.2

Cloud or Self Hosted

Self Hosted (Docker)

Steps to reproduce

In the same conversation, input variables cannot be updated by using the chatflow api.

✔️ Expected Behavior

input variables can be updated by setting the input param in the chatflow api.

❌ Actual Behavior

input variables cannot be updated by using the chatflow api.

dosubot[bot] commented 2 weeks ago

@hanhaiszw Hello there! I'm a bot here to assist you with bugs, questions, and becoming a contributor. While you wait for a human maintainer, feel free to ask me anything.

I found a similar issue that might be relevant to your problem: seems that some variables are lost after the Parameter Extraction node. This issue is closed and was last updated on August 01, 2024. The suggested solutions include ensuring correct input variable selection, proper model configuration, accurate parameter definition, and correct node configuration, among other things.

For updating input variables in a conversation, the VariableAssignerNode class and the update_conversation_variable function are responsible for handling this. The VariableAssignerNode supports different write modes (OVER_WRITE, APPEND, CLEAR) to update variables. Additionally, the useChatWithHistory hook in web/app/components/base/chat/chat-with-history/hooks.tsx manages conversation states and updates input variables.

Here is a relevant code snippet from the VariableAssignerNode class:

class VariableAssignerNode(BaseNode):
    # ...

    def _run(self, variable_pool: VariablePool) -> NodeRunResult:
        data = cast(VariableAssignerData, self.node_data)

        # Should be String, Number, Object, ArrayString, ArrayNumber, ArrayObject
        original_variable = variable_pool.get(data.assigned_variable_selector)
        if not isinstance(original_variable, Variable):
            raise VariableAssignerNodeError('assigned variable not found')

        match data.write_mode:
            case WriteMode.OVER_WRITE:
                income_value = variable_pool.get(data.input_variable_selector)
                if not income_value:
                    raise VariableAssignerNodeError('input value not found')
                updated_variable = original_variable.model_copy(update={'value': income_value.value})

            case WriteMode.APPEND:
                income_value = variable_pool.get(data.input_variable_selector)
                if not income_value:
                    raise VariableAssignerNodeError('input value not found')
                updated_value = original_variable.value + [income_value.value]
                updated_variable = original_variable.model_copy(update({'value': updated_value})

            case WriteMode.CLEAR:
                income_value = get_zero_value(original_variable.value_type)
                updated_variable = original_variable.model_copy(update({'value': income_value.to_object()})

            case _:
                raise VariableAssignerNodeError(f'unsupported write mode: {data.write_mode}')

        # Over write the variable.
        variable_pool.add(data.assigned_variable_selector, updated_variable)
        # Update conversation variable.
        conversation_id = variable_pool.get(['sys', 'conversation_id'])
        if not conversation_id:
            raise VariableAssignerNodeError('conversation_id not found')
        update_conversation_variable(conversation_id=conversation_id.text, variable=updated_variable)

        return NodeRunResult(
            status=WorkflowNodeExecutionStatus.SUCCEEDED,
            inputs={
                'value': income_value.to_object(),
            },
        )

def update_conversation_variable(conversation_id: str, variable: Variable):
    stmt = select(ConversationVariable).where(
        ConversationVariable.id == variable.id, ConversationVariable.conversation_id == conversation_id
    )
    with Session(db.engine) as session:
        row = session.scalar(stmt)
        if not row:
            raise VariableAssignerNodeError('conversation variable not found in the database')
        row.data = variable.model_dump_json()
        session.commit()

Additionally, the useChatWithHistory hook manages conversation states and updates input variables:

const handleNewConversationInputsChange = useCallback((newInputs: Record<string, any>) => {
    newConversationInputsRef.current = newInputs
    setNewConversationInputs(newInputs)
}, [])

const handleChangeConversation = useCallback((conversationId: string) => {
    currentChatInstanceRef.current.handleStop()
    setNewConversationId('')
    handleConversationIdInfoChange(conversationId)

    if (conversationId === '' && !checkInputsRequired(true))
        setShowConfigPanelBeforeChat(true)
    else
        setShowConfigPanelBeforeChat(false)
}, [handleConversationIdInfoChange, setShowConfigPanelBeforeChat, checkInputsRequired])

These methods ensure that input variables are correctly updated and managed throughout the conversation lifecycle [1][2][3].

To continue talking to Dosu, mention @dosu.