ProCreations commited on
Commit
4c88467
·
verified ·
1 Parent(s): 85d767b

Upload scripts/tree_utils.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. scripts/tree_utils.py +74 -0
scripts/tree_utils.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Structural parsing shared by data generation, training, and evaluation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import re
7
+ from dataclasses import dataclass
8
+
9
+ REF_RE = re.compile(r"\[ref=([^\]]+)\]")
10
+ STAGEHAND_REF_RE = re.compile(r"^\s*\[([^\]]+)\]")
11
+ WORD_RE = re.compile(r"[a-z0-9]{2,}")
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class Window:
16
+ id: str
17
+ text: str
18
+ refs: tuple[str, ...]
19
+ start_line: int
20
+ end_line: int
21
+
22
+
23
+ def references(text: str, source_format: str = "betterwright") -> set[str]:
24
+ if source_format == "stagehand":
25
+ return {m.group(1) for line in text.splitlines() if (m := STAGEHAND_REF_RE.match(line))}
26
+ # Auto-detect mixed corpora: Stagehand's outline is `[id] role: name`,
27
+ # while BetterWright/Playwright use `[ref=id]`. The patterns cannot collide.
28
+ result = set(REF_RE.findall(text))
29
+ result.update(
30
+ m.group(1) for line in text.splitlines() if (m := STAGEHAND_REF_RE.match(line))
31
+ )
32
+ return result
33
+
34
+
35
+ def words(text: str) -> set[str]:
36
+ return set(WORD_RE.findall(text.casefold()))
37
+
38
+
39
+ def structural_windows(text: str, *, max_chars: int = 3600, overlap_lines: int = 4) -> list[Window]:
40
+ """Create overlapping, indentation-aware windows without cutting a line."""
41
+ lines = [line.rstrip() for line in text.splitlines() if line.strip()]
42
+ if not lines:
43
+ return []
44
+ windows: list[Window] = []
45
+ start = 0
46
+ while start < len(lines):
47
+ size = 0
48
+ end = start
49
+ while end < len(lines) and (size + len(lines[end]) + 1 <= max_chars or end == start):
50
+ size += len(lines[end]) + 1
51
+ end += 1
52
+ # Prefer a structural boundary near the limit.
53
+ if end < len(lines):
54
+ floor = max(start + 1, end - 12)
55
+ candidates = [i for i in range(floor, end) if len(lines[i]) - len(lines[i].lstrip()) <= 2]
56
+ if candidates:
57
+ end = candidates[-1]
58
+ body = "\n".join(lines[start:end])
59
+ refs = tuple(sorted(references(body)))
60
+ digest = hashlib.sha1(f"{start}\0{end}\0{body}".encode()).hexdigest()[:16]
61
+ windows.append(Window(digest, body, refs, start, end))
62
+ if end >= len(lines):
63
+ break
64
+ start = max(start + 1, end - overlap_lines)
65
+ return windows
66
+
67
+
68
+ def lexical_score(query: str, text: str) -> float:
69
+ q = words(query)
70
+ if not q:
71
+ return 0.0
72
+ t = words(text)
73
+ overlap = len(q & t)
74
+ return overlap / (len(q) ** 0.5 * max(1, len(t)) ** 0.25)