feat(customs): 新增 ExpressionRecognition 表达式识别 custom 教程 - #8
Conversation
Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Hey - 我发现了 2 个问题
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="Storage/customs/overflow65537/expression-recognition/ExpressionRecognition.py" line_range="183-168" />
<code_context>
+ return box_index
+ return None
+
+ def _extract_text(self, result: Any, box_index: int | None = None) -> str | None:
+ if box_index is not None:
+ indexed_text = self._extract_text_by_index(result, box_index)
+ if indexed_text is not None:
+ return indexed_text
+
+ if isinstance(result, dict):
+ direct_text = result.get("text")
+ if isinstance(direct_text, str):
+ return direct_text
+
+ for key in ("best", "best_result", "detail", "filtered", "filtered_results", "all"):
+ if key in result:
+ nested_text = self._extract_text(result[key])
+ if nested_text is not None:
+ return nested_text
+ return None
+
+ if isinstance(result, Iterable) and not isinstance(result, (str, bytes, bytearray)):
</code_context>
<issue_to_address>
**issue:** Guard against potential cycles in recognition objects during text extraction
这些辅助函数会在多个属性之间递归(`detail`、`raw_detail`、`best_result`、`filtered_results`、`sub_results` 等)。如果识别对象图出现循环,就可能导致无限递归并造成栈溢出。建议在递归调用过程中传递一个包含对象 ID 的 `visited` 集合,并跳过已经访问过的对象,以此在保持当前无环图行为不变的前提下,对可能的循环进行防护。
</issue_to_address>
### Comment 2
<location path="Storage/customs/overflow65537/expression-recognition/ExpressionRecognition.py" line_range="406-409" />
<code_context>
+ return text
+ return f"{text[:limit]}...<truncated>"
+
+ def _normalize_expression(self, expression: str) -> str:
+ normalized = expression.replace("&&", " and ").replace("||", " or ")
+ normalized = re.sub(r"!(?!=)", " not ", normalized)
+ return normalized
+
+ def _validate_ast(self, node: ast.AST, allowed_names: set[str]) -> None:
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Clarify or tighten normalization around logical operators and negation
使用 `re.sub(r"!(?!=)", " not ", normalized)` 也会把 `a!b` 重写为 `a not b`,这样任何出现在非布尔或紧邻标识符场景中的 `!` 都会被默默接受,而不是被拒绝。建议将其限制为单独的 `!` 记号(例如要求前后有空白)或者在解析前增加预验证步骤,对意外的 `!` 用法抛出更清晰的错误。
```suggestion
def _normalize_expression(self, expression: str) -> str:
# Reject clearly invalid uses of "!" (e.g. in the middle of identifiers or operands),
# instead of silently treating them as logical negation.
#
# Example invalid patterns: "a!b", "foo!bar"
# Valid patterns that are still supported:
# "!foo", "a && !b", "x != y"
if re.search(r"\w!\w", expression):
raise ValueError(
"Invalid '!' usage in expression: '!' may only be used as boolean "
"negation (e.g. '!x') or as part of '!=' comparison."
)
normalized = expression.replace("&&", " and ").replace("||", " or ")
# Normalize standalone logical negation, but leave '!=' untouched.
normalized = re.sub(r"!(?!=)", " not ", normalized)
return normalized
```
</issue_to_address>帮我变得更有用!请在每条评论上点击 👍 或 👎,我会根据这些反馈改进对你代码的评审。
Original comment in English
Hey - I've found 2 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="Storage/customs/overflow65537/expression-recognition/ExpressionRecognition.py" line_range="183-168" />
<code_context>
+ return box_index
+ return None
+
+ def _extract_text(self, result: Any, box_index: int | None = None) -> str | None:
+ if box_index is not None:
+ indexed_text = self._extract_text_by_index(result, box_index)
+ if indexed_text is not None:
+ return indexed_text
+
+ if isinstance(result, dict):
+ direct_text = result.get("text")
+ if isinstance(direct_text, str):
+ return direct_text
+
+ for key in ("best", "best_result", "detail", "filtered", "filtered_results", "all"):
+ if key in result:
+ nested_text = self._extract_text(result[key])
+ if nested_text is not None:
+ return nested_text
+ return None
+
+ if isinstance(result, Iterable) and not isinstance(result, (str, bytes, bytearray)):
</code_context>
<issue_to_address>
**issue:** Guard against potential cycles in recognition objects during text extraction
These helpers recurse through several attributes (`detail`, `raw_detail`, `best_result`, `filtered_results`, `sub_results`, etc.). If the recognition object graph ever becomes cyclic, this can lead to unbounded recursion and a stack overflow. Consider passing a `visited` set of object ids through the recursive calls and skipping already‑seen objects to guard against cycles while preserving current behavior for acyclic graphs.
</issue_to_address>
### Comment 2
<location path="Storage/customs/overflow65537/expression-recognition/ExpressionRecognition.py" line_range="406-409" />
<code_context>
+ return text
+ return f"{text[:limit]}...<truncated>"
+
+ def _normalize_expression(self, expression: str) -> str:
+ normalized = expression.replace("&&", " and ").replace("||", " or ")
+ normalized = re.sub(r"!(?!=)", " not ", normalized)
+ return normalized
+
+ def _validate_ast(self, node: ast.AST, allowed_names: set[str]) -> None:
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Clarify or tighten normalization around logical operators and negation
Using `re.sub(r"!(?!=)", " not ", normalized)` will also rewrite `a!b` to `a not b`, so any stray `!` in non‑boolean or adjacent‑identifier contexts is silently accepted instead of rejected. Consider restricting this to `!` as a separate token (e.g., with surrounding whitespace) or adding a pre‑validation step that raises a clearer error for unexpected `!` usage before parsing.
```suggestion
def _normalize_expression(self, expression: str) -> str:
# Reject clearly invalid uses of "!" (e.g. in the middle of identifiers or operands),
# instead of silently treating them as logical negation.
#
# Example invalid patterns: "a!b", "foo!bar"
# Valid patterns that are still supported:
# "!foo", "a && !b", "x != y"
if re.search(r"\w!\w", expression):
raise ValueError(
"Invalid '!' usage in expression: '!' may only be used as boolean "
"negation (e.g. '!x') or as part of '!=' comparison."
)
normalized = expression.replace("&&", " and ").replace("||", " or ")
# Normalize standalone logical negation, but leave '!=' untouched.
normalized = re.sub(r"!(?!=)", " not ", normalized)
return normalized
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
|
|
||
| def _get_box_index(self, node_data: Any) -> int | None: | ||
| if not isinstance(node_data, dict): | ||
| return None |
There was a problem hiding this comment.
issue: Guard against potential cycles in recognition objects during text extraction
这些辅助函数会在多个属性之间递归(detail、raw_detail、best_result、filtered_results、sub_results 等)。如果识别对象图出现循环,就可能导致无限递归并造成栈溢出。建议在递归调用过程中传递一个包含对象 ID 的 visited 集合,并跳过已经访问过的对象,以此在保持当前无环图行为不变的前提下,对可能的循环进行防护。
Original comment in English
issue: Guard against potential cycles in recognition objects during text extraction
These helpers recurse through several attributes (detail, raw_detail, best_result, filtered_results, sub_results, etc.). If the recognition object graph ever becomes cyclic, this can lead to unbounded recursion and a stack overflow. Consider passing a visited set of object ids through the recursive calls and skipping already‑seen objects to guard against cycles while preserving current behavior for acyclic graphs.
| def _normalize_expression(self, expression: str) -> str: | ||
| normalized = expression.replace("&&", " and ").replace("||", " or ") | ||
| normalized = re.sub(r"!(?!=)", " not ", normalized) | ||
| return normalized |
There was a problem hiding this comment.
suggestion (bug_risk): Clarify or tighten normalization around logical operators and negation
使用 re.sub(r"!(?!=)", " not ", normalized) 也会把 a!b 重写为 a not b,这样任何出现在非布尔或紧邻标识符场景中的 ! 都会被默默接受,而不是被拒绝。建议将其限制为单独的 ! 记号(例如要求前后有空白)或者在解析前增加预验证步骤,对意外的 ! 用法抛出更清晰的错误。
| def _normalize_expression(self, expression: str) -> str: | |
| normalized = expression.replace("&&", " and ").replace("||", " or ") | |
| normalized = re.sub(r"!(?!=)", " not ", normalized) | |
| return normalized | |
| def _normalize_expression(self, expression: str) -> str: | |
| # Reject clearly invalid uses of "!" (e.g. in the middle of identifiers or operands), | |
| # instead of silently treating them as logical negation. | |
| # | |
| # Example invalid patterns: "a!b", "foo!bar" | |
| # Valid patterns that are still supported: | |
| # "!foo", "a && !b", "x != y" | |
| if re.search(r"\w!\w", expression): | |
| raise ValueError( | |
| "Invalid '!' usage in expression: '!' may only be used as boolean " | |
| "negation (e.g. '!x') or as part of '!=' comparison." | |
| ) | |
| normalized = expression.replace("&&", " and ").replace("||", " or ") | |
| # Normalize standalone logical negation, but leave '!=' untouched. | |
| normalized = re.sub(r"!(?!=)", " not ", normalized) | |
| return normalized |
Original comment in English
suggestion (bug_risk): Clarify or tighten normalization around logical operators and negation
Using re.sub(r"!(?!=)", " not ", normalized) will also rewrite a!b to a not b, so any stray ! in non‑boolean or adjacent‑identifier contexts is silently accepted instead of rejected. Consider restricting this to ! as a separate token (e.g., with surrounding whitespace) or adding a pre‑validation step that raises a clearer error for unexpected ! usage before parsing.
| def _normalize_expression(self, expression: str) -> str: | |
| normalized = expression.replace("&&", " and ").replace("||", " or ") | |
| normalized = re.sub(r"!(?!=)", " not ", normalized) | |
| return normalized | |
| def _normalize_expression(self, expression: str) -> str: | |
| # Reject clearly invalid uses of "!" (e.g. in the middle of identifiers or operands), | |
| # instead of silently treating them as logical negation. | |
| # | |
| # Example invalid patterns: "a!b", "foo!bar" | |
| # Valid patterns that are still supported: | |
| # "!foo", "a && !b", "x != y" | |
| if re.search(r"\w!\w", expression): | |
| raise ValueError( | |
| "Invalid '!' usage in expression: '!' may only be used as boolean " | |
| "negation (e.g. '!x') or as part of '!=' comparison." | |
| ) | |
| normalized = expression.replace("&&", " and ").replace("||", " or ") | |
| # Normalize standalone logical negation, but leave '!=' untouched. | |
| normalized = re.sub(r"!(?!=)", " not ", normalized) | |
| return normalized |
Summary by Sourcery
添加一个新的 ExpressionRecognition 自定义识别器,用于评估基于 OCR 的数值表达式,并提供相应的使用文档以及示例管线/元数据文件。
New Features:
Documentation:
Chores:
Original summary in English
Summary by Sourcery
Add a new ExpressionRecognition custom recognizer that evaluates OCR-based numeric expressions and provide accompanying usage documentation and sample pipeline/metadata files.
New Features:
Documentation:
Chores: