@roll-agent/octopus-agent 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@roll-agent/octopus-agent",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "丸子Agent(Octopus Agent)",
5
5
  "license": "UNLICENSED",
6
6
  "keywords": [
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "octopus-skill"
3
- version = "0.1.1"
3
+ version = "0.1.2"
4
4
  description = "丸子Agent(Octopus Agent)"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.1.1"
3
+ __version__ = "0.1.2"
@@ -970,18 +970,55 @@ def _concept_time_range_field_issue(
970
970
  return None
971
971
 
972
972
 
973
+ _TIME_RANGE_DATE_LITERAL = r"'20\d{2}-\d{1,2}-\d{1,2}(?:\s+\d{1,2}:\d{2}:\d{2})?'"
974
+
975
+
973
976
  def _sql_has_time_filter_for_range(sql: str, time_range: TimeRange) -> bool:
974
- where_index = _find_top_level_keyword(sql, "where", start=0)
975
- if where_index is None:
976
- return False
977
- clause_start = where_index + len("where")
978
- clause_end = _where_filter_insert_pos(sql[clause_start:])
979
- if clause_end < len(sql[clause_start:]):
980
- clause_end += clause_start
981
- else:
982
- clause_end = len(sql)
983
- for predicate in _split_top_level_and_predicates(sql[clause_start:clause_end].strip()):
984
- if _is_time_range_predicate(predicate, sql=sql, time_range=time_range):
977
+ if _scope_has_time_filter_for_range(sql, time_range):
978
+ return True
979
+ for subquery in _sql_derived_subqueries(sql).values():
980
+ if _sql_has_time_filter_for_range(subquery, time_range):
981
+ return True
982
+ return False
983
+
984
+
985
+ def _mask_derived_subqueries(sql: str) -> str:
986
+ spans = _sql_derived_subquery_spans(sql)
987
+ if not spans:
988
+ return sql
989
+ chars = list(sql)
990
+ for start, end in spans:
991
+ for index in range(start, min(end, len(chars))):
992
+ chars[index] = " "
993
+ return "".join(chars)
994
+
995
+
996
+ def _scope_has_time_filter_for_range(sql: str, time_range: TimeRange) -> bool:
997
+ """Return True if the designated time field is compared to a date literal in this
998
+ query scope.
999
+
1000
+ Nested derived subqueries are masked out here and evaluated separately by
1001
+ ``_sql_has_time_filter_for_range`` so alias resolution stays scoped. The check
1002
+ scans the whole scope (not just the top-level WHERE) so the field can satisfy the
1003
+ requirement whether it is used in WHERE or inside a conditional aggregate such as
1004
+ ``SUM(CASE WHEN sign_up_time >= ... THEN 1 ELSE 0 END)``, which some schema concepts
1005
+ mandate for period metrics.
1006
+ """
1007
+ scope_text = _mask_derived_subqueries(sql)
1008
+ op = r"(?:>=|<=|>|<|=)"
1009
+ date = _TIME_RANGE_DATE_LITERAL
1010
+ field_patterns: list[str] = [_time_range_sql_field_pattern(scope_text, time_range)]
1011
+ base_tables = set(_sql_top_level_table_aliases(scope_text).values())
1012
+ if not time_range.table_name or base_tables == {time_range.table_name.lower()}:
1013
+ # Single base table in scope: an unqualified column reference is unambiguous.
1014
+ field_patterns.append(rf"(?<![.\w]){re.escape(time_range.field.lower())}\b")
1015
+ for field_pattern in field_patterns:
1016
+ checks = (
1017
+ rf"{field_pattern}\s*{op}\s*{date}",
1018
+ rf"{date}\s*{op}\s*{field_pattern}",
1019
+ rf"{field_pattern}\s+between\s+{date}\s+and\s+{date}",
1020
+ )
1021
+ if any(re.search(pattern, scope_text, flags=re.IGNORECASE) for pattern in checks):
985
1022
  return True
986
1023
  return False
987
1024