Skip to content

修复 And/Or 内联子识别导出后无法重新加载的问题 - #1423

Open
Whning0513 wants to merge 1 commit into
MaaXYZ:mainfrom
Whning0513:fix/issue-1314-sub-recognition-roundtrip
Open

修复 And/Or 内联子识别导出后无法重新加载的问题#1423
Whning0513 wants to merge 1 commit into
MaaXYZ:mainfrom
Whning0513:fix/issue-1314-sub-recognition-roundtrip

Conversation

@Whning0513

@Whning0513 Whning0513 commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

你好,

这次主要改了什么

这次主要是修了一个 And/Or 内联子识别导出后无法重新加载的问题啊。

简单来说的话,现在 PipelineDumper 会把内联子识别的 typeparam 直接放在 sub_name 旁边。

问题就是,PipelineParser 读取时,需要把它们放在 recognition 对象里。然后,从 get_node_data 导出的结果重新加载时,结构就会不对,最后导致解析失败。

修改内容

  • 修正 And 内联子识别的导出结构;
  • 修正 Or 内联子识别的导出结构;
  • 为 And 内联子识别增加 Parser/Dumper round-trip 测试;
  • 保留普通节点引用的原有导出方式。

检查

  • 已通过 clang-format、Python 语法和 git diff --check 检查;
  • 已增加 And 内联子识别的 round-trip 测试;
  • GitHub Actions 会在 PR 中重新运行。

Fixes #1314 — whning#0513 官服

Summary by Sourcery

修复内联 And/Or 子识别的导出格式,以便导出的流水线可以被正确重新加载。

Bug 修复:

  • 更正内联 And 子识别的导出方式,将其导出为嵌套的 recognition 对象,而不是将 type/param 扁平化。
  • 调整内联 Or 子识别的导出结构,使其与用于解析的预期嵌套 recognition 格式保持一致。

测试:

  • 添加一个 Python 往返测试,用于导出包含内联 And 子识别的流水线,并验证其可以再次成功加载。
Original summary in English

Summary by Sourcery

Fix inline And/Or sub-recognition export format so dumped pipelines can be reloaded correctly.

Bug Fixes:

  • Correct And inline sub-recognition to be exported under a nested recognition object instead of flattening type/param.
  • Align Or inline sub-recognition export structure with the expected nested recognition format for parsing.

Tests:

  • Add a Python round-trip test that dumps an And inline sub-recognition pipeline and verifies it can be loaded again.

Copilot AI review requested due to automatic review settings July 29, 2026 18:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 1 个问题,并给出了一些高层次的反馈:

  • 由于 dumper 逻辑同时更新了 And 和 Or 的内联子识别逻辑,建议为 Or 也添加一个类似的往返(round-trip)测试,以捕获其导出/导入路径中的回归问题。
  • 在新的往返测试中,除了验证重新加载成功外,你可以增加一个对导出 JSON 结构的断言(例如检查 sub_name 和嵌套的 recognition 是否存在),以便更早发现结构上的回归。
面向 AI 代理的提示
Please address the comments from this code review:

## Overall Comments
- Since the dumper logic was updated for both And and Or inline sub recognitions, consider adding an analogous round-trip test for Or to catch regressions in its export/import path.
- In the new round-trip test, you might add an assertion on the dumped JSON structure (e.g., presence of `sub_name` and nested `recognition`) in addition to successful reload, so structural regressions are caught earlier.

## Individual Comments

### Comment 1
<location path="test/python/pipeline_test.py" line_range="182-183" />
<code_context>
+        resource.post_bundle(str(source_dir)).wait()
+        assert_true(resource.loaded, "source resource should load")
+
+        dumped = resource.get_node_data("AndTask")
+        assert_not_none(dumped, "dumped And node")
+
</code_context>
<issue_to_address>
**suggestion (testing):** Strengthen the test by asserting the dumped structure of the inline sub-recognition before reloading.

Currently the test only verifies that the bundle reloads after a dump, which doesn’t confirm the JSON layout itself. Please add assertions on `dumped` before writing it out, e.g. checking `dumped["recognition"]["param"]["all_of"][0]["sub_name"] == "OCR1"` and that the nested `"recognition"` object has the expected `"type"` and `"param"` fields, to lock in the contract that fixed the bug.

```suggestion
        resource.post_bundle(str(source_dir)).wait()
        assert_true(resource.loaded, "source resource should load")

        dumped = resource.get_node_data("AndTask")
        assert_not_none(dumped, "dumped And node")

        # Verify dumped inline sub-recognition structure before writing it out
        recognition = dumped.get("recognition")
        assert_not_none(recognition, "AndTask should have recognition config")
        assert_true(
            isinstance(recognition, dict),
            "recognition should be a dict",
        )

        param = recognition.get("param")
        assert_not_none(param, "recognition should have param")
        assert_true(
            isinstance(param, dict),
            "recognition.param should be a dict",
        )

        all_of = param.get("all_of")
        assert_true(
            isinstance(all_of, list) and len(all_of) > 0,
            "recognition.param.all_of should be a non-empty list",
        )

        first = all_of[0]
        assert_true(
            isinstance(first, dict),
            "first all_of entry should be a dict",
        )
        assert_true(
            first.get("sub_name") == "OCR1",
            'first all_of entry should have sub_name == "OCR1"',
        )

        sub_recognition = first.get("recognition")
        assert_not_none(
            sub_recognition,
            "inline sub-recognition should have nested recognition object",
        )
        assert_true(
            isinstance(sub_recognition, dict),
            "nested recognition should be a dict",
        )
        assert_true(
            "type" in sub_recognition,
            "nested recognition should have type field",
        )
        assert_true(
            "param" in sub_recognition,
            "nested recognition should have param field",
        )
```
</issue_to_address>

Sourcery 对开源项目是免费的——如果你觉得我们的评审有帮助,请考虑分享 ✨
帮我变得更有用!请对每条评论点 👍 或 👎,我会根据反馈改进后续的评审。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • Since the dumper logic was updated for both And and Or inline sub recognitions, consider adding an analogous round-trip test for Or to catch regressions in its export/import path.
  • In the new round-trip test, you might add an assertion on the dumped JSON structure (e.g., presence of sub_name and nested recognition) in addition to successful reload, so structural regressions are caught earlier.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Since the dumper logic was updated for both And and Or inline sub recognitions, consider adding an analogous round-trip test for Or to catch regressions in its export/import path.
- In the new round-trip test, you might add an assertion on the dumped JSON structure (e.g., presence of `sub_name` and nested `recognition`) in addition to successful reload, so structural regressions are caught earlier.

## Individual Comments

### Comment 1
<location path="test/python/pipeline_test.py" line_range="182-183" />
<code_context>
+        resource.post_bundle(str(source_dir)).wait()
+        assert_true(resource.loaded, "source resource should load")
+
+        dumped = resource.get_node_data("AndTask")
+        assert_not_none(dumped, "dumped And node")
+
</code_context>
<issue_to_address>
**suggestion (testing):** Strengthen the test by asserting the dumped structure of the inline sub-recognition before reloading.

Currently the test only verifies that the bundle reloads after a dump, which doesn’t confirm the JSON layout itself. Please add assertions on `dumped` before writing it out, e.g. checking `dumped["recognition"]["param"]["all_of"][0]["sub_name"] == "OCR1"` and that the nested `"recognition"` object has the expected `"type"` and `"param"` fields, to lock in the contract that fixed the bug.

```suggestion
        resource.post_bundle(str(source_dir)).wait()
        assert_true(resource.loaded, "source resource should load")

        dumped = resource.get_node_data("AndTask")
        assert_not_none(dumped, "dumped And node")

        # Verify dumped inline sub-recognition structure before writing it out
        recognition = dumped.get("recognition")
        assert_not_none(recognition, "AndTask should have recognition config")
        assert_true(
            isinstance(recognition, dict),
            "recognition should be a dict",
        )

        param = recognition.get("param")
        assert_not_none(param, "recognition should have param")
        assert_true(
            isinstance(param, dict),
            "recognition.param should be a dict",
        )

        all_of = param.get("all_of")
        assert_true(
            isinstance(all_of, list) and len(all_of) > 0,
            "recognition.param.all_of should be a non-empty list",
        )

        first = all_of[0]
        assert_true(
            isinstance(first, dict),
            "first all_of entry should be a dict",
        )
        assert_true(
            first.get("sub_name") == "OCR1",
            'first all_of entry should have sub_name == "OCR1"',
        )

        sub_recognition = first.get("recognition")
        assert_not_none(
            sub_recognition,
            "inline sub-recognition should have nested recognition object",
        )
        assert_true(
            isinstance(sub_recognition, dict),
            "nested recognition should be a dict",
        )
        assert_true(
            "type" in sub_recognition,
            "nested recognition should have type field",
        )
        assert_true(
            "param" in sub_recognition,
            "nested recognition should have param field",
        )
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +182 to +183
resource.post_bundle(str(source_dir)).wait()
assert_true(resource.loaded, "source resource should load")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): 通过在重新加载之前断言内联子识别的导出结构来增强测试。

目前该测试只验证了在导出之后 bundle 能否重新加载,这并不能确认 JSON 布局本身。请在写出 dumped 之前对其添加断言,例如检查 dumped["recognition"]["param"]["all_of"][0]["sub_name"] == "OCR1",以及嵌套的 "recognition" 对象是否具有预期的 "type""param" 字段,从而锁定修复该 bug 的约定。

Suggested change
resource.post_bundle(str(source_dir)).wait()
assert_true(resource.loaded, "source resource should load")
resource.post_bundle(str(source_dir)).wait()
assert_true(resource.loaded, "source resource should load")
dumped = resource.get_node_data("AndTask")
assert_not_none(dumped, "dumped And node")
# Verify dumped inline sub-recognition structure before writing it out
recognition = dumped.get("recognition")
assert_not_none(recognition, "AndTask should have recognition config")
assert_true(
isinstance(recognition, dict),
"recognition should be a dict",
)
param = recognition.get("param")
assert_not_none(param, "recognition should have param")
assert_true(
isinstance(param, dict),
"recognition.param should be a dict",
)
all_of = param.get("all_of")
assert_true(
isinstance(all_of, list) and len(all_of) > 0,
"recognition.param.all_of should be a non-empty list",
)
first = all_of[0]
assert_true(
isinstance(first, dict),
"first all_of entry should be a dict",
)
assert_true(
first.get("sub_name") == "OCR1",
'first all_of entry should have sub_name == "OCR1"',
)
sub_recognition = first.get("recognition")
assert_not_none(
sub_recognition,
"inline sub-recognition should have nested recognition object",
)
assert_true(
isinstance(sub_recognition, dict),
"nested recognition should be a dict",
)
assert_true(
"type" in sub_recognition,
"nested recognition should have type field",
)
assert_true(
"param" in sub_recognition,
"nested recognition should have param field",
)
Original comment in English

suggestion (testing): Strengthen the test by asserting the dumped structure of the inline sub-recognition before reloading.

Currently the test only verifies that the bundle reloads after a dump, which doesn’t confirm the JSON layout itself. Please add assertions on dumped before writing it out, e.g. checking dumped["recognition"]["param"]["all_of"][0]["sub_name"] == "OCR1" and that the nested "recognition" object has the expected "type" and "param" fields, to lock in the contract that fixed the bug.

Suggested change
resource.post_bundle(str(source_dir)).wait()
assert_true(resource.loaded, "source resource should load")
resource.post_bundle(str(source_dir)).wait()
assert_true(resource.loaded, "source resource should load")
dumped = resource.get_node_data("AndTask")
assert_not_none(dumped, "dumped And node")
# Verify dumped inline sub-recognition structure before writing it out
recognition = dumped.get("recognition")
assert_not_none(recognition, "AndTask should have recognition config")
assert_true(
isinstance(recognition, dict),
"recognition should be a dict",
)
param = recognition.get("param")
assert_not_none(param, "recognition should have param")
assert_true(
isinstance(param, dict),
"recognition.param should be a dict",
)
all_of = param.get("all_of")
assert_true(
isinstance(all_of, list) and len(all_of) > 0,
"recognition.param.all_of should be a non-empty list",
)
first = all_of[0]
assert_true(
isinstance(first, dict),
"first all_of entry should be a dict",
)
assert_true(
first.get("sub_name") == "OCR1",
'first all_of entry should have sub_name == "OCR1"',
)
sub_recognition = first.get("recognition")
assert_not_none(
sub_recognition,
"inline sub-recognition should have nested recognition object",
)
assert_true(
isinstance(sub_recognition, dict),
"nested recognition should be a dict",
)
assert_true(
"type" in sub_recognition,
"nested recognition should have type field",
)
assert_true(
"param" in sub_recognition,
"nested recognition should have param field",
)

@MistEO

MistEO commented Jul 31, 2026

Copy link
Copy Markdown
Member

CI 单测没过,看看咋回事?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] PipelineDumper 输出 sub_recognition 字段嵌套层级错误,parser 无法重新加载 dumper 自己的输出

3 participants