1515@dataclass
1616class PatchMetrics :
1717 """Patch basic metrics"""
18+
1819 occurrence_count : int = 1
1920
2021
2122@dataclass
2223class NormalizedPatch :
2324 """Normalized patch data structure"""
25+
2426 original_index : int
2527 original_content : str
2628 normalized_content : str
@@ -29,95 +31,96 @@ class NormalizedPatch:
2931
3032class PatchNormalizationNode :
3133 """Patch Normalization and Direct Selection Node
32-
34+
3335 Implements patch normalization, deduplication and direct best patch selection.
3436 Simplified approach without complex voting mechanisms.
3537 """
36-
38+
3739 def __init__ (self ):
3840 self ._logger = logging .getLogger (
3941 f"thread-{ threading .get_ident ()} .prometheus.lang_graph.nodes.patch_normalization_node"
4042 )
41-
43+
4244 def normalize_patch (self , raw_patch : str ) -> str :
4345 """Normalize patch content for deduplication
44-
46+
4547 Removes metadata lines and standardizes formatting to enable
4648 accurate patch comparison and deduplication.
4749 """
4850 if not raw_patch :
4951 return ""
50-
51- lines = raw_patch .split (' \n ' )
52+
53+ lines = raw_patch .split (" \n " )
5254 normalized_lines = []
53-
55+
5456 for line in lines :
5557 # Skip metadata lines
5658 if self ._is_metadata_line (line ):
5759 continue
58-
60+
5961 # Normalize file paths
60- if line .startswith (' --- ' ) or line .startswith (' +++ ' ):
62+ if line .startswith (" --- " ) or line .startswith (" +++ " ):
6163 line = self ._normalize_file_path (line )
62-
64+
6365 normalized_lines .append (line )
64-
65- return ' \n ' .join (normalized_lines )
66-
66+
67+ return " \n " .join (normalized_lines )
68+
6769 def _is_metadata_line (self , line : str ) -> bool :
6870 """Check if line is metadata that should be ignored"""
6971 metadata_patterns = [
70- r' ^diff --git' ,
71- r' ^index [a-f0-9]+\.\.[a-f0-9]+' ,
72- r' ^new file mode \d+' ,
73- r' ^deleted file mode \d+' ,
74- r' ^similarity index \d+%' ,
75- r' ^rename from ' ,
76- r' ^rename to ' ,
77- r' ^Binary files ' ,
72+ r" ^diff --git" ,
73+ r" ^index [a-f0-9]+\.\.[a-f0-9]+" ,
74+ r" ^new file mode \d+" ,
75+ r" ^deleted file mode \d+" ,
76+ r" ^similarity index \d+%" ,
77+ r" ^rename from " ,
78+ r" ^rename to " ,
79+ r" ^Binary files " ,
7880 ]
79-
81+
8082 return any (re .match (pattern , line ) for pattern in metadata_patterns )
81-
83+
8284 def _normalize_file_path (self , line : str ) -> str :
8385 """Normalize file path in diff header"""
8486 # Remove timestamp and mode information
85- line = re .sub (r' \s+\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(\.\d+)? \+\d{4}' , '' , line )
86- line = re .sub (r' \s+\d{6}' , '' , line )
87-
87+ line = re .sub (r" \s+\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(\.\d+)? \+\d{4}" , "" , line )
88+ line = re .sub (r" \s+\d{6}" , "" , line )
89+
8890 return line
89-
91+
9092 def calculate_patch_metrics (self , normalized_patch : str ) -> PatchMetrics :
9193 """Calculate basic metrics for a patch"""
9294 return PatchMetrics ()
93-
94-
95+
9596 def deduplicate_patches (self , patches : List [str ]) -> List [NormalizedPatch ]:
9697 """Deduplicate patches using normalization
97-
98+
9899 Returns list of unique normalized patches with occurrence counts.
99100 """
100101 if not patches :
101102 return []
102-
103+
103104 # Normalize all patches
104105 normalized_patches = []
105106 for i , patch in enumerate (patches ):
106107 normalized_content = self .normalize_patch (patch )
107108 metrics = self .calculate_patch_metrics (normalized_content )
108-
109- normalized_patches .append (NormalizedPatch (
110- original_index = i ,
111- original_content = patch ,
112- normalized_content = normalized_content ,
113- metrics = metrics
114- ))
115-
109+
110+ normalized_patches .append (
111+ NormalizedPatch (
112+ original_index = i ,
113+ original_content = patch ,
114+ normalized_content = normalized_content ,
115+ metrics = metrics ,
116+ )
117+ )
118+
116119 # Group by normalized content
117120 patch_groups = defaultdict (list )
118121 for patch in normalized_patches :
119122 patch_groups [patch .normalized_content ].append (patch )
120-
123+
121124 # Create deduplicated list with occurrence counts
122125 deduplicated = []
123126 for normalized_content , group in patch_groups .items ():
@@ -126,40 +129,44 @@ def deduplicate_patches(self, patches: List[str]) -> List[NormalizedPatch]:
126129 # Update occurrence count
127130 representative .metrics .occurrence_count = len (group )
128131 deduplicated .append (representative )
129-
130- self ._logger .info (f"Deduplication complete: { len (patches )} -> { len (deduplicated )} unique patches" )
131-
132+
133+ self ._logger .info (
134+ f"Deduplication complete: { len (patches )} -> { len (deduplicated )} unique patches"
135+ )
136+
132137 return deduplicated
133-
138+
134139 def __call__ (self , state : Dict ) -> Dict :
135140 """Node call interface
136-
141+
137142 Process edit_patches in state, return normalized, deduplicated patches and selected best patch
138143 """
139144 patches = state .get ("edit_patches" , [])
140-
145+
141146 if not patches :
142147 self ._logger .warning ("No patches found to process" )
143148 return {
144149 "normalized_patches" : [],
145150 "final_patch" : "" ,
146151 "original_patch_count" : 0 ,
147- "unique_patch_count" : 0
152+ "unique_patch_count" : 0 ,
148153 }
149-
154+
150155 self ._logger .info (f"Starting to process { len (patches )} patches" )
151-
156+
152157 # Execute deduplication and normalization
153158 normalized_patches = self .deduplicate_patches (patches )
154-
159+
155160 # Return deduplicated patches (selection will be done by final_patch_selection_node)
156161 deduplicated_patches = [patch .original_content for patch in normalized_patches ]
157-
158- self ._logger .info (f"Patch processing complete, deduplicated to { len (normalized_patches )} unique patches" )
159-
162+
163+ self ._logger .info (
164+ f"Patch processing complete, deduplicated to { len (normalized_patches )} unique patches"
165+ )
166+
160167 return {
161168 "normalized_patches" : normalized_patches ,
162169 "edit_patches" : deduplicated_patches , # Return deduplicated patches for selection
163170 "original_patch_count" : len (patches ),
164- "unique_patch_count" : len (normalized_patches )
171+ "unique_patch_count" : len (normalized_patches ),
165172 }
0 commit comments