Closed AndyMik90 closed 4 months ago
To resolve the TypeError: Object of type FileVar is not JSON serializable
and ensure the iteration node correctly recognizes an array of files in your workflow, you need to use the jsonable_encoder
function to encode the outputs before serializing them to JSON. This function is already imported and used in the _get_next_iteration
method.
Here's how you can ensure the iteration node correctly recognizes an array of files and handles the serialization:
iterator
is a list of files.jsonable_encoder
to encode the outputs before returning them.FileVar
using the handle_special_values
method to convert them to a serializable format.The relevant part of the code that handles the serialization is in the _get_next_iteration
method:
def _get_next_iteration(self, variable_pool: VariablePool, state: IterationState) -> NodeRunResult | str:
"""
Get next iteration start node id based on the graph.
:param graph: graph
:return: next node id
"""
# resolve current output
self._resolve_current_output(variable_pool, state)
# move to next iteration
self._next_iteration(variable_pool, state)
node_data = cast(IterationNodeData, self.node_data)
if self._reached_iteration_limit(variable_pool, state):
return NodeRunResult(
status: WorkflowNodeExecutionStatus.SUCCEEDED,
outputs={
'output': jsonable_encoder(self.handle_special_values(state.outputs)) # Ensure outputs are JSON serializable
}
)
return node_data.start_node_id
By using jsonable_encoder
and handle_special_values
, you ensure that the state.outputs
are properly encoded and can be serialized to JSON, thus avoiding the TypeError
. The handle_special_values
method converts FileVar
objects to a dictionary format that is JSON serializable.
Additionally, ensure that the IterationNodeType
correctly handles the iterator_selector
and output
variables. The getNodeOutputVars
function should include the output
variable for the Iteration
block:
case BlockEnum.Iteration: {
res.push([id, 'output'])
break
}
This ensures that the iteration node correctly recognizes and processes the array of files.
In the useConfig
hook, ensure that the iterator
and output
variables are correctly set and handled:
const handleRun = useCallback((data: Record<string, any>) => {
const formattedData: Record<string, any> = {}
Object.keys(allVarObject).forEach((key) => {
const [varSectorStr, nodeId] = key.split(DELIMITER)
formattedData[`${nodeId}.${allVarObject[key].inSingleRunPassedKey}`] = data[varSectorStr]
})
formattedData[iteratorInputKey] = data[iteratorInputKey]
doHandleRun(formattedData)
}, [allVarObject, doHandleRun, iteratorInputKey])
const iterator = runInputData[iteratorInputKey]
const setIterator = useCallback((newIterator: string[]) => {
setRunInputData({
...runInputData,
[iteratorInputKey]: newIterator,
})
}, [iteratorInputKey, runInputData, setRunInputData])
This ensures that the iteration node correctly recognizes and processes the array of files [1][2][3][4].
To continue talking to Dosu, mention @dosu.
Update, also found an error when using the workflow as a tool in an agent; the output ArrayFiles gives this error.
ERROR:root:Instance <MessageFile at 0x7ff53fcf11b0> is not bound to a Session; attribute refresh operation cannot proceed (Background on this error at: https://sqlalche.me/e/20/bhk3)
The error Instance <MessageFile at 0x7ff53fcf11b0> is not bound to a Session
typically occurs when an instance of a SQLAlchemy model is accessed outside of an active session. Here are steps to resolve this issue:
Ensure Session Binding: Make sure that the MessageFile
instance is bound to an active session when performing operations. You can reattach the instance to a session if needed.
session.add(message_file_instance)
session.commit()
Use Scoped Sessions: Use SQLAlchemy's scoped_session
to ensure that the session is properly managed and instances remain bound to the session.
from sqlalchemy.orm import scoped_session, sessionmaker
session_factory = sessionmaker(bind=engine)
Session = scoped_session(session_factory)
session = Session()
Eager Loading: Use eager loading to avoid lazy loading issues.
message_files = session.query(MessageFile).options(joinedload(MessageFile.some_relationship)).all()
Check Session Lifecycle: Ensure that the session is active and not closed when performing operations on the instance.
if not session.is_active:
session = Session()
In the context of the MessageFileParser
class, ensure that the session is properly managed when querying or manipulating MessageFile
objects. For example, when validating and transforming files, the session should be active and properly scoped to avoid detached instances:
upload_file = (db.session.query(UploadFile)
.filter(
UploadFile.id == file_obj.related_id,
UploadFile.tenant_id == self.tenant_id,
UploadFile.created_by == user.id,
UploadFile.created_by_role == ('account' if isinstance(user, Account) else 'end_user'),
UploadFile.extension.in_(IMAGE_EXTENSIONS)
).first())
if not upload_file:
raise ValueError('Invalid upload file')
By following these practices, you can avoid the "Instance is not bound to a Session" error in the context of the MessageFile
class [1][2].
To continue talking to Dosu, mention @dosu.
If you need any additional information @takatost just reach out to me. It's a bit urgent for us to fix this as we have a new agent needing to produce images with a workflow tool.
Self Checks
Dify version
0.6.9
Cloud or Self Hosted
Cloud, Self Hosted (Docker), Self Hosted (Source)
Steps to reproduce
DSL to reproduce:
✔️ Expected Behavior
Output the files in an array so multiple files can be outputted in the end node.
❌ Actual Behavior
TypeError: Object of type FileVar is not JSON serializable and wrong Array.