diff --git a/src/oagi/utils/output_parser.py b/src/oagi/utils/output_parser.py index 86dddf5..711a0b8 100644 --- a/src/oagi/utils/output_parser.py +++ b/src/oagi/utils/output_parser.py @@ -114,7 +114,8 @@ def _parse_action(action_text: str) -> Action | None: Action object or None if parsing fails """ # Match action format: action_type(arguments) - match = re.match(r"(\w+)\((.*)\)", action_text.strip()) + # re.DOTALL allows '.' to match newlines for multiline type() content + match = re.match(r"(\w+)\((.*)\)", action_text.strip(), re.DOTALL) if not match: return None diff --git a/tests/utils/test_output_parser.py b/tests/utils/test_output_parser.py index 965d4fe..9de73f5 100644 --- a/tests/utils/test_output_parser.py +++ b/tests/utils/test_output_parser.py @@ -109,6 +109,14 @@ def test_parse_invalid_action_skipped(self): assert step.actions[0].type == ActionType.CLICK assert step.actions[1].type == ActionType.TYPE + def test_parse_type_with_multiline_content(self): + raw = "<|think_start|>Type multiline<|think_end|>\n<|action_start|>type(line1\nline2\nline3)<|action_end|>" + step = parse_raw_output(raw) + + assert len(step.actions) == 1 + assert step.actions[0].type == ActionType.TYPE + assert step.actions[0].argument == "line1\nline2\nline3" + class TestSplitActions: """Test _split_actions function.""" @@ -141,6 +149,11 @@ def test_split_three_actions(self): result = _split_actions("click(100, 200) & type(hello) & finish()") assert result == ["click(100, 200)", "type(hello)", "finish()"] + def test_split_multiline_type_preserved(self): + """Multiline content inside type() parens is NOT split.""" + result = _split_actions("type(line1\nline2) & click(100, 200)") + assert result == ["type(line1\nline2)", "click(100, 200)"] + class TestParseAction: """Test _parse_action function.""" @@ -221,6 +234,12 @@ def test_parse_hotkey_invalid_count_defaults_to_one(self): assert action is not None assert action.count == 1 + def test_parse_type_with_multiline_content(self): + action = _parse_action("type(first line\nsecond line\nthird line)") + assert action is not None + assert action.type == ActionType.TYPE + assert action.argument == "first line\nsecond line\nthird line" + class TestEdgeCases: """Test edge cases and robustness."""