Complete the report by optimizing the content and references. This method: 1. Collects all items from the report 2. Merges and deduplicates all references 3. Renumbers citations in content and references 4. Uses LLM to generate a complete markdown rep
(self)
| 149 | return report_item |
| 150 | |
| 151 | async def complete(self): |
| 152 | """Complete the report by optimizing the content and references. |
| 153 | |
| 154 | This method: |
| 155 | 1. Collects all items from the report |
| 156 | 2. Merges and deduplicates all references |
| 157 | 3. Renumbers citations in content and references |
| 158 | 4. Uses LLM to generate a complete markdown report |
| 159 | 5. Writes the report to the file path specified during initialization |
| 160 | |
| 161 | Raises: |
| 162 | ValueError: If report_file_path is not set or report has no items |
| 163 | """ |
| 164 | if not self.items: |
| 165 | raise ValueError("Cannot complete report: no items found") |
| 166 | |
| 167 | if not self.report_file_path: |
| 168 | raise ValueError("Cannot complete report: report_file_path is not set") |
| 169 | |
| 170 | # Step 1: Collect all unique references from all items |
| 171 | # Deduplicate by both description and URL to handle similar references |
| 172 | all_references_dict: Dict[str, ReferenceItem] = {} # normalized_key -> ReferenceItem |
| 173 | reference_key_to_id: Dict[str, int] = {} # normalized_key -> first_seen_id |
| 174 | |
| 175 | def normalize_reference_key(ref: ReferenceItem) -> str: |
| 176 | """Create a normalized key for deduplication based on description and URL.""" |
| 177 | # Normalize description: strip whitespace, lowercase for comparison |
| 178 | desc = ref.description.strip().lower() if ref.description else "" |
| 179 | |
| 180 | # Normalize URL: extract and normalize URL |
| 181 | url = ref.url.strip().lower() if ref.url else "" |
| 182 | |
| 183 | # If URL exists, use URL as primary key (more reliable for deduplication) |
| 184 | if url: |
| 185 | # Normalize URL: remove trailing slashes, convert to lowercase |
| 186 | url_normalized = url.rstrip('/') |
| 187 | return f"url:{url_normalized}" |
| 188 | |
| 189 | # If no URL but description looks like a URL, use it |
| 190 | if desc.startswith(('http://', 'https://', 'file://')): |
| 191 | desc_normalized = desc.rstrip('/') |
| 192 | return f"url:{desc_normalized}" |
| 193 | |
| 194 | # Otherwise, use normalized description |
| 195 | return f"desc:{desc}" |
| 196 | |
| 197 | for item in self.items: |
| 198 | for ref in item.references: |
| 199 | normalized_key = normalize_reference_key(ref) |
| 200 | |
| 201 | # If we've seen this reference before, merge information |
| 202 | if normalized_key in all_references_dict: |
| 203 | existing_ref = all_references_dict[normalized_key] |
| 204 | # Prefer non-empty values: use URL if available, otherwise keep existing |
| 205 | if ref.url and not existing_ref.url: |
| 206 | existing_ref.url = ref.url |
| 207 | # Prefer more descriptive description |
| 208 | if ref.description and len(ref.description) > len(existing_ref.description): |
no test coverage detected