Compare commits

...

3 Commits

Author SHA1 Message Date
openhands b4b7c30d4d Fix linting issues: remove trailing whitespace and apply ruff formatting
Co-authored-by: openhands <openhands@all-hands.dev>
2025-08-05 21:44:25 +00:00
openhands 0fc6956977 Add regression test for AgentFinishAction serialization bug
This test demonstrates and prevents regression of the bug where
task_completed field would appear in serialized output even though
it was removed from the AgentFinishAction dataclass.

The test ensures that:
1. AgentFinishAction instances serialize correctly without task_completed
2. The serialized output matches the expected format
3. No extra fields are added during serialization
2025-08-05 21:27:42 +00:00
openhands 0e608a47be Fix AgentFinishAction serialization bug with task_completed field
The task_completed field was removed from AgentFinishAction in commit c2fc84e6e,
but instances were still getting this field dynamically added during pytest runs,
causing serialization/deserialization tests to fail.

This fix ensures that task_completed is always removed from the serialized output
in event_to_dict() for backward compatibility, preventing the field from appearing
in serialized AgentFinishAction instances.

Fixes test_agent_finish_action_serialization_deserialization test failure.

Related to issue #10099
2025-08-05 21:19:26 +00:00
2 changed files with 51 additions and 2 deletions
+4 -2
View File
@@ -121,8 +121,10 @@ def event_to_dict(event: 'Event') -> dict:
props.pop(key, None)
if 'security_risk' in props and props['security_risk'] is None:
props.pop('security_risk')
# Remove task_completed from serialization when it's None (backward compatibility)
if 'task_completed' in props and props['task_completed'] is None:
# Remove task_completed from serialization (backward compatibility)
# task_completed field was removed from AgentFinishAction but may still appear
# in some instances due to legacy data or test artifacts
if 'task_completed' in props:
props.pop('task_completed')
if 'action' in d:
d['args'] = props
+47
View File
@@ -105,6 +105,53 @@ def test_agent_finish_action_legacy_task_completed_serialization():
assert 'task_completed' not in event_dict['args']
def test_agent_finish_action_serialization_bug_demonstration():
"""
This test demonstrates and prevents regression of the bug where event_to_dict
would include task_completed: None in the serialized args even though the
AgentFinishAction dataclass doesn't have this field.
The bug occurred because during pytest execution, AgentFinishAction instances
were getting a task_completed=None attribute added dynamically, which would
then be included in the serialized output by asdict().
"""
# Create a simple AgentFinishAction
original_action_dict = {
'action': 'finish',
'args': {
'outputs': {},
'thought': '',
'final_thought': '',
},
}
# Deserialize the action
action_instance = event_from_dict(original_action_dict)
# The action should be properly deserialized
assert isinstance(action_instance, AgentFinishAction)
# Now serialize it back
serialized_dict = event_to_dict(action_instance)
# Remove the message field as other tests do
serialized_dict.pop('message')
# This assertion should pass - the serialized dict should match the original
# If this fails, it means the bug has regressed and task_completed is being added
assert serialized_dict == original_action_dict, (
f'Serialization bug detected: serialized dict {serialized_dict} != '
f'original dict {original_action_dict}. '
f'Extra fields in serialized args: '
f'{set(serialized_dict.get("args", {}).keys()) - set(original_action_dict.get("args", {}).keys())}'
)
# Specifically check that task_completed is not in the serialized args
assert 'task_completed' not in serialized_dict.get('args', {}), (
'task_completed field should not appear in serialized AgentFinishAction args'
)
def test_agent_reject_action_serialization_deserialization():
original_action_dict = {
'action': 'reject',