bioguider 0.2.33__py3-none-any.whl → 0.2.34__py3-none-any.whl

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.

Potentially problematic release.


This version of bioguider might be problematic. Click here for more details.

@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  from typing import Dict
4
4
  import json
5
5
  import re
6
+ import os
6
7
  from langchain_openai.chat_models.base import BaseChatOpenAI
7
8
 
8
9
  from bioguider.agents.common_conversation import CommonConversation
@@ -19,7 +20,6 @@ INPUTS (use only what is provided; never invent)
19
20
  - suggestion_category: {suggestion_category}
20
21
  - anchor_title: {anchor_title}
21
22
  - guidance: {guidance}
22
- - evidence_from_evaluation: {evidence}
23
23
  - repo_context_excerpt (analyze tone/formatting; do not paraphrase it blindly): <<{context}>>
24
24
 
25
25
  CRITICAL REQUIREMENTS
@@ -33,7 +33,7 @@ CRITICAL REQUIREMENTS
33
33
  - ABSOLUTELY FORBIDDEN: Do NOT add summary sections, notes, conclusions, or any text at the end of documents
34
34
  - ABSOLUTELY FORBIDDEN: Do NOT wrap content in markdown code fences (```markdown). Return pure content only.
35
35
  - ABSOLUTELY FORBIDDEN: Do NOT add phrases like "Happy analyzing!", "Ensure all dependencies are up-to-date", or any concluding statements
36
- - ALWAYS use the specific guidance provided above to create concrete, actionable content based on evidence
36
+ - ALWAYS use the specific guidance provided above to create concrete, actionable content
37
37
 
38
38
  STYLE & CONSTRAINTS
39
39
  - Fix obvious errors in the content.
@@ -79,7 +79,7 @@ LLM_FULLDOC_PROMPT = """
79
79
  You are "BioGuider," a documentation rewriter with enhanced capabilities for complex documents.
80
80
 
81
81
  GOAL
82
- Rewrite a complete target document using only the provided evaluation report signals and the repository context excerpts. Output a full, ready-to-publish markdown file that is more complete and directly usable. You now have increased token capacity to handle complex documents comprehensively.
82
+ Rewrite a complete target document by enhancing the existing content while maintaining the EXACT original structure, sections, and flow. Use only the provided evaluation report signals and repository context excerpts. Output a full, ready-to-publish markdown file that follows the original document structure precisely while incorporating improvements. You now have increased token capacity to handle complex documents comprehensively.
83
83
 
84
84
  INPUTS (authoritative)
85
85
  - evaluation_report (structured JSON excerpts): <<{evaluation_report}>>
@@ -94,11 +94,12 @@ This file requires improvements from {total_suggestions} separate evaluation sug
94
94
  4. **Write the document ONCE** with all improvements incorporated throughout
95
95
 
96
96
  INTEGRATION STRATEGY
97
- - Identify which suggestions target similar topics (e.g., setup, reproducibility, performance)
98
- - Group related improvements and apply them to the same document sections
99
- - For tutorial files: Enhance existing sections with all relevant suggestions, don't create duplicate sections
97
+ - **CRITICAL**: Follow the EXACT structure of the original document. Do NOT create new sections.
98
+ - Identify which suggestions target existing sections in the original document
99
+ - Apply improvements ONLY to existing sections - do NOT create new sections
100
+ - For tutorial files: Enhance existing sections with relevant suggestions, maintain original section order
100
101
  - For documentation files: Merge suggestions into existing structure, avoid redundant sections
101
- - Result: ONE enhanced document that addresses all {total_suggestions} suggestions simultaneously
102
+ - Result: ONE enhanced document that follows the original structure and addresses all {total_suggestions} suggestions
102
103
 
103
104
  CAPACITY AND SCOPE
104
105
  - You have enhanced token capacity to handle complex documents comprehensively
@@ -107,23 +108,24 @@ CAPACITY AND SCOPE
107
108
  - Comprehensive documents: Full capacity for complete documentation with all necessary sections
108
109
 
109
110
  STRICT CONSTRAINTS
111
+ - **CRITICAL**: Follow the EXACT structure and sections of the original document. Do NOT create new sections or reorganize content.
110
112
  - Base the content solely on the evaluation report and repo context. Do not invent features, data, or claims not supported by these sources.
111
113
  - CRITICAL: NEVER invent technical specifications including:
112
114
  * Hardware requirements (RAM, CPU, disk space) unless explicitly stated in guidance/context
113
115
  * Version numbers for dependencies unless explicitly stated in guidance/context
114
116
  * Performance metrics, benchmarks, or timing estimates
115
- * Biological/computational parameters or thresholds without evidence
117
+ * Biological/computational parameters or thresholds without substantiation
116
118
  * Installation commands or package names not found in the repo context
117
- - Prefer completeness and usability: produce the full file content, not just minimal "added" snippets.
118
- - Preserve top-of-file badges/logos if they exist in the original; keep title and header area intact unless the report requires changes.
119
- - CRITICAL: Preserve the original document structure, sections, and flow. Only enhance existing content and add missing information.
120
- - For tutorial files, maintain all original sections while improving clarity and adding missing details based on evaluation suggestions.
119
+ - **CRITICAL**: Preserve the original document structure, sections, and flow EXACTLY. Only enhance existing content and add missing information based on evaluation suggestions.
120
+ - For tutorial files, maintain ALL original sections in their original order while improving clarity and adding missing details based on evaluation suggestions.
121
121
  - Fix obvious errors; improve structure and readability per report suggestions.
122
- - Include ONLY sections specifically requested by the evaluation report - do not add unnecessary sections.
122
+ - Include ONLY sections that exist in the original document - do not add unnecessary sections.
123
123
  - Avoid redundancy: do not duplicate information across multiple sections.
124
- - ABSOLUTELY FORBIDDEN: Do NOT add summary sections, notes, conclusions, or any text at the end of documents
125
- - ABSOLUTELY FORBIDDEN: Do NOT wrap the entire document inside markdown code fences (```markdown). Do NOT start with ```markdown or end with ```. Return pure content suitable for copy/paste.
126
- - ABSOLUTELY FORBIDDEN: Do NOT add phrases like "Happy analyzing!" or any concluding statements
124
+ - **ABSOLUTELY CRITICAL**: Do NOT add ANY conclusion, summary, or closing paragraph at the end
125
+ - **ABSOLUTELY CRITICAL**: Do NOT wrap the entire document inside markdown code fences (```markdown). Do NOT start with ```markdown or end with ```. Return pure content suitable for copy/paste.
126
+ - **ABSOLUTELY CRITICAL**: Do NOT add phrases like "Happy analyzing!", "This vignette demonstrates...", "By following the steps outlined...", or ANY concluding statements
127
+ - **ABSOLUTELY CRITICAL**: Stop writing IMMEDIATELY after the last content section from the original document. Do NOT add "## Conclusion", "## Summary", or any final paragraphs
128
+ - **CRITICAL**: Do NOT reorganize, rename, or create new sections. Follow the original document structure exactly.
127
129
  - Keep links well-formed; keep neutral, professional tone; concise, skimmable formatting.
128
130
  - Preserve file-specific formatting (e.g., YAML frontmatter, code fence syntax) and do not wrap content in extra code fences.
129
131
 
@@ -182,12 +184,47 @@ OUTPUT
182
184
  - Return only the full README.md content. No commentary, no fences.
183
185
  """
184
186
 
187
+ # Continuation prompt template - used when document generation is truncated
188
+ LLM_CONTINUATION_PROMPT = """
189
+ You are "BioGuider," continuing a truncated documentation generation task.
190
+
191
+ IMPORTANT: This is STRICT CONTINUATION ONLY. You are NOT creating new content.
192
+ You are NOT adding conclusions or summaries. You are ONLY completing the missing sections from the original document.
193
+
194
+ PREVIOUS CONTENT (do not repeat this):
195
+ ```
196
+ {existing_content_tail}
197
+ ```
198
+
199
+ STRICT CONTINUATION RULES:
200
+ - Examine the previous content above and identify what section it ends with
201
+ - Continue IMMEDIATELY after that section with the next missing section from the original document
202
+ - Use the EXACT same structure, style, and tone as the existing content
203
+ - Add ONLY the specific content that should logically follow from the last section
204
+ - Do NOT add ANY conclusions, summaries, additional resources, or wrap-up content
205
+ - Do NOT add phrases like "For further guidance", "Additional Resources", or "In conclusion"
206
+
207
+ MISSING CONTENT TO ADD:
208
+ Based on typical RMarkdown vignette structure, if the document ends with "Common Pitfalls", you should add:
209
+ - SCT integration example (SCTransform section)
210
+ - Session info section
211
+ - Details section (if present in original)
212
+ - STOP after these sections - do NOT add anything else
213
+
214
+ CRITICAL: STOP IMMEDIATELY after completing the missing sections from the original document.
215
+ Do NOT add "## Additional Resources" or any final sections.
216
+
217
+ OUTPUT:
218
+ - Return ONLY the continuation content that completes the original document structure
219
+ - No commentary, no fences, no conclusions, no additional content
220
+ """
221
+
185
222
 
186
223
  class LLMContentGenerator:
187
224
  def __init__(self, llm: BaseChatOpenAI):
188
225
  self.llm = llm
189
226
 
190
- def _detect_truncation(self, content: str, target_file: str) -> bool:
227
+ def _detect_truncation(self, content: str, target_file: str, original_content: str = None) -> bool:
191
228
  """
192
229
  Detect if content appears to be truncated based on common patterns.
193
230
  Universal detection for all file types.
@@ -195,6 +232,7 @@ class LLMContentGenerator:
195
232
  Args:
196
233
  content: Generated content to check
197
234
  target_file: Target file path for context
235
+ original_content: Original content for comparison (if available)
198
236
 
199
237
  Returns:
200
238
  True if content appears truncated, False otherwise
@@ -202,19 +240,27 @@ class LLMContentGenerator:
202
240
  if not content or len(content.strip()) < 100:
203
241
  return True
204
242
 
205
- # 1. Check for very short content (applies to all files)
206
- # Only flag as truncated if content is very short (< 1500 chars)
207
- if len(content) < 1500:
243
+ # 1. Compare to original length if available (most reliable indicator)
244
+ if original_content:
245
+ original_len = len(original_content)
246
+ generated_len = len(content)
247
+ # If generated content is significantly shorter than original (< 80%), likely truncated
248
+ if generated_len < original_len * 0.8:
249
+ return True
250
+
251
+ # 2. Check for very short content (applies to all files)
252
+ # Only flag as truncated if content is very short (< 500 chars)
253
+ if len(content) < 500:
208
254
  return True
209
255
 
210
- # 2. Check for incomplete code blocks (any language)
256
+ # 3. Check for incomplete code blocks (any language)
211
257
  # Count opening and closing code fences
212
258
  code_fence_count = content.count('```')
213
259
  if code_fence_count > 0 and code_fence_count % 2 != 0:
214
260
  # Unbalanced code fences suggest truncation
215
261
  return True
216
262
 
217
- # 3. Check for specific language code blocks
263
+ # 4. Check for specific language code blocks
218
264
  if target_file.endswith('.Rmd'):
219
265
  # R chunks should be complete
220
266
  r_chunks_open = re.findall(r'```\{r[^}]*\}', content)
@@ -278,14 +324,91 @@ class LLMContentGenerator:
278
324
 
279
325
  return False
280
326
 
281
- def _appears_complete(self, content: str, target_file: str) -> bool:
327
+ def _find_continuation_point(self, content: str, original_content: str = None) -> str:
328
+ """
329
+ Find a better continuation point than just the last 1000 characters.
330
+ Looks for the last complete section or code block to continue from.
331
+
332
+ Args:
333
+ content: The generated content so far
334
+ original_content: The original content for comparison
335
+
336
+ Returns:
337
+ A suitable continuation point, or None if not found
282
338
  """
283
- Check if content appears to be complete based on structure and patterns.
339
+ if not content:
340
+ return None
341
+
342
+ lines = content.split('\n')
343
+ if len(lines) < 10: # Too short to find good continuation point
344
+ return None
345
+
346
+ # Strategy 1: Find the last complete section (header with content after it)
347
+ for i in range(len(lines) - 1, -1, -1):
348
+ line = lines[i].strip()
349
+ if line.startswith('## ') and i + 1 < len(lines):
350
+ # Check if there's content after this header
351
+ next_lines = []
352
+ for j in range(i + 1, min(i + 10, len(lines))): # Look at next 10 lines
353
+ if lines[j].strip() and not lines[j].strip().startswith('##'):
354
+ next_lines.append(lines[j])
355
+ else:
356
+ break
357
+
358
+ if next_lines: # Found header with content after it
359
+ # Return from this header onwards
360
+ return '\n'.join(lines[i:])
361
+
362
+ # Strategy 2: Find the last complete code block
363
+ in_code_block = False
364
+ code_block_start = -1
365
+
366
+ for i in range(len(lines) - 1, -1, -1):
367
+ line = lines[i].strip()
368
+ if line.startswith('```') and not in_code_block:
369
+ in_code_block = True
370
+ code_block_start = i
371
+ elif line.startswith('```') and in_code_block:
372
+ # Found complete code block
373
+ return '\n'.join(lines[code_block_start:])
374
+
375
+ # Strategy 3: Find last complete paragraph (ends with period)
376
+ for i in range(len(lines) - 1, -1, -1):
377
+ line = lines[i].strip()
378
+ if line and line.endswith('.') and not line.startswith('#') and not line.startswith('```'):
379
+ # Found a complete sentence, return from there
380
+ return '\n'.join(lines[i:])
381
+
382
+ # Strategy 4: If original content is available, find where the generated content diverges
383
+ if original_content:
384
+ # Simple approach: find the longest common suffix
385
+ min_len = min(len(content), len(original_content))
386
+ common_length = 0
387
+
388
+ for i in range(1, min_len + 1):
389
+ if content[-i:] == original_content[-i:]:
390
+ common_length = i
391
+ else:
392
+ break
393
+
394
+ if common_length > 100: # Found significant common ending
395
+ return content[-(common_length + 100):] # Include some context
396
+
397
+ return None
398
+
399
+ def _appears_complete(self, content: str, target_file: str, original_content: str = None) -> bool:
400
+ """
401
+ Check if content appears to be complete based on structure, patterns, AND original length.
284
402
  Universal completion check for all file types.
285
403
 
404
+ CRITICAL: If original_content is provided, generated content MUST be at least 90% of original length
405
+ to be considered complete, regardless of other heuristics. This prevents the LLM from fooling us
406
+ with fake conclusions.
407
+
286
408
  Args:
287
409
  content: Generated content to check
288
410
  target_file: Target file path for context
411
+ original_content: Original content for length comparison (optional but recommended)
289
412
 
290
413
  Returns:
291
414
  True if content appears complete, False if it needs continuation
@@ -293,6 +416,15 @@ class LLMContentGenerator:
293
416
  if not content or len(content.strip()) < 100:
294
417
  return False
295
418
 
419
+ # CRITICAL: If original content is provided, check length ratio first
420
+ # This prevents the LLM from fooling us with fake conclusions
421
+ if original_content and isinstance(original_content, str):
422
+ generated_len = len(content)
423
+ original_len = len(original_content)
424
+ if generated_len < original_len * 0.9:
425
+ # Generated content is too short compared to original - NOT complete
426
+ return False
427
+
296
428
  # 1. Check for balanced code blocks (applies to all files)
297
429
  code_block_count = content.count('```')
298
430
  if code_block_count > 0 and code_block_count % 2 != 0:
@@ -441,61 +573,14 @@ class LLMContentGenerator:
441
573
  elif "suggestions" in evaluation_report and isinstance(evaluation_report["suggestions"], list):
442
574
  total_suggestions = len(evaluation_report["suggestions"])
443
575
 
444
- continuation_prompt = f"""
445
- You are "BioGuider," continuing a documentation generation task with enhanced capacity for complex documents.
446
-
447
- GOAL
448
- Continue generating the document "{target_file}" from where the previous generation left off.
449
- The previous content was truncated and needs to be completed. You now have increased token
450
- capacity to handle complex documents comprehensively.
451
-
452
- PREVIOUS CONTENT (do not repeat this):
453
- ```
454
- {existing_content[-1000:]} # Last 1000 chars for context
455
- ```
456
-
457
- TASK
458
- Continue the document naturally from the last complete section. Maintain the same style,
459
- structure, and flow as the previous content. Complete all remaining sections that should
460
- be in this document.
461
-
462
- CAPACITY AND SCOPE
463
- - You have enhanced token capacity to handle complex documents comprehensively
464
- - Tutorial documents: Enhanced capacity for step-by-step content, code examples, and comprehensive explanations
465
- - Complex documents: Increased capacity for multiple sections, detailed explanations, and extensive content
466
- - Comprehensive documents: Full capacity for complete documentation with all necessary sections
467
-
468
- INPUTS
469
- - evaluation_report (contains {total_suggestions} suggestions to integrate): {json.dumps(evaluation_report)[:4000]}
470
- - context: {context[:2000]}
471
-
472
- REMINDER: SINGLE DOCUMENT APPROACH
473
- - The evaluation report contains {total_suggestions} SEPARATE suggestions
474
- - These should be integrated into ONE cohesive continuation
475
- - Do NOT create {total_suggestions} separate sections for each suggestion
476
- - Group related suggestions (e.g., setup, reproducibility, performance) and integrate them naturally
477
-
478
- REQUIREMENTS
479
- - Continue seamlessly from the previous content
480
- - Maintain the same tone and style
481
- - Complete all sections that should be in this document
482
- - Preserve file-specific formatting (e.g., YAML frontmatter, code block syntax appropriate to the language)
483
- - Do not repeat content already generated
484
- - Return only the continuation content, not the full document
485
- - Use the increased token capacity to provide thorough, complete content
486
- - NEVER invent technical specifications (hardware, versions, performance) unless explicitly in evaluation report or context
487
- - ABSOLUTELY FORBIDDEN: Do NOT wrap content in markdown code fences (```markdown). Return pure content only.
488
- - ABSOLUTELY FORBIDDEN: Do NOT add summary sections, notes, conclusions, or any text at the end of documents
489
-
490
- COMPLETENESS REQUIREMENTS
491
- - Generate complete, comprehensive content that addresses all remaining evaluation suggestions
492
- - For complex documents, ensure all sections are fully developed and detailed
493
- - For tutorial documents, include complete step-by-step instructions with examples
494
- - Use the increased token capacity to provide thorough, useful documentation
495
-
496
- OUTPUT
497
- Return only the continuation content that should be appended to the existing content.
498
- """
576
+ # Use the centralized continuation prompt template
577
+ continuation_prompt = LLM_CONTINUATION_PROMPT.format(
578
+ target_file=target_file,
579
+ existing_content_tail=existing_content[-1000:], # Last 1000 chars for context
580
+ total_suggestions=total_suggestions,
581
+ evaluation_report_excerpt=json.dumps(evaluation_report)[:4000],
582
+ context_excerpt=context[:2000],
583
+ )
499
584
 
500
585
  content, token_usage = conv.generate(
501
586
  system_prompt=continuation_prompt,
@@ -514,14 +599,13 @@ Return only the continuation content that should be appended to the existing con
514
599
  section=section_name,
515
600
  anchor_title=section_name,
516
601
  suggestion_category=suggestion.category,
517
- evidence=(suggestion.source.get("evidence", "") if suggestion.source else ""),
518
602
  context=context[:2500],
519
603
  guidance=(suggestion.content_guidance or "").strip(),
520
604
  )
521
605
  content, token_usage = conv.generate(system_prompt=system_prompt, instruction_prompt="Write the section content now.")
522
606
  return content.strip(), token_usage
523
607
 
524
- def generate_full_document(self, target_file: str, evaluation_report: dict, context: str = "") -> tuple[str, dict]:
608
+ def generate_full_document(self, target_file: str, evaluation_report: dict, context: str = "", original_content: str = None) -> tuple[str, dict]:
525
609
  # Create LLM (uses 16k tokens by default - enough for any document)
526
610
  from bioguider.agents.agent_utils import get_llm
527
611
  import os
@@ -560,6 +644,11 @@ Return only the continuation content that should be appended to the existing con
560
644
  with open(debug_file, 'w', encoding='utf-8') as f:
561
645
  json.dump(debug_info, f, indent=2, ensure_ascii=False)
562
646
 
647
+ # Debug: Save raw evaluation_report to see what's being serialized
648
+ eval_report_file = os.path.join(debug_dir, f"{safe_filename}_raw_eval_report.json")
649
+ with open(eval_report_file, 'w', encoding='utf-8') as f:
650
+ json.dump(evaluation_report, f, indent=2, ensure_ascii=False)
651
+
563
652
  # Use comprehensive README prompt for README.md files
564
653
  if target_file.endswith("README.md"):
565
654
  system_prompt = LLM_README_COMPREHENSIVE_PROMPT.format(
@@ -590,14 +679,24 @@ Return only the continuation content that should be appended to the existing con
590
679
  f.write(system_prompt)
591
680
  f.write("\n\n=== INSTRUCTION PROMPT ===\n")
592
681
  f.write("Write the full document now.")
593
- f.write("\n\n=== EVALUATION REPORT ===\n")
594
- f.write(json.dumps(evaluation_report, indent=2))
595
- f.write("\n\n=== CONTEXT ===\n")
596
- f.write(context[:2000] + "..." if len(context) > 2000 else context)
682
+ # Context is already embedded in system prompt; avoid duplicating here
597
683
 
598
684
  # Initial generation
599
- content, token_usage = conv.generate(system_prompt=system_prompt, instruction_prompt="Write the full document now.")
600
- content = content.strip()
685
+ # If the original document is long (RMarkdown > 8k chars), avoid truncation by chunked rewrite
686
+ # Lower threshold from 12k to 8k to catch more documents that would otherwise truncate
687
+ use_chunked = bool(target_file.endswith('.Rmd') and isinstance(original_content, str) and len(original_content) > 8000)
688
+ if use_chunked:
689
+ content, token_usage = self._generate_full_document_chunked(
690
+ target_file=target_file,
691
+ evaluation_report=evaluation_report,
692
+ context=context,
693
+ original_content=original_content or "",
694
+ debug_dir=debug_dir,
695
+ safe_filename=safe_filename,
696
+ )
697
+ else:
698
+ content, token_usage = conv.generate(system_prompt=system_prompt, instruction_prompt="Write the full document now.")
699
+ content = content.strip()
601
700
 
602
701
  # Save initial generation for debugging
603
702
  generation_file = os.path.join(debug_dir, f"{safe_filename}_generation_0.txt")
@@ -605,7 +704,9 @@ Return only the continuation content that should be appended to the existing con
605
704
  f.write(f"=== INITIAL GENERATION ===\n")
606
705
  f.write(f"Tokens: {token_usage}\n")
607
706
  f.write(f"Length: {len(content)} characters\n")
608
- f.write(f"Truncation detected: {self._detect_truncation(content, target_file)}\n")
707
+ if original_content:
708
+ f.write(f"Original length: {len(original_content)} characters\n")
709
+ f.write(f"Truncation detected: {self._detect_truncation(content, target_file, original_content)}\n")
609
710
  f.write(f"\n=== CONTENT ===\n")
610
711
  f.write(content)
611
712
 
@@ -613,79 +714,39 @@ Return only the continuation content that should be appended to the existing con
613
714
  max_continuations = 3 # Limit to prevent infinite loops
614
715
  continuation_count = 0
615
716
 
616
- while (self._detect_truncation(content, target_file) and
717
+ while (not use_chunked and self._detect_truncation(content, target_file, original_content) and
617
718
  continuation_count < max_continuations):
618
719
 
619
720
  # Additional check: if content appears complete, don't continue
620
- if self._appears_complete(content, target_file):
721
+ # Pass original_content so we can check length ratio
722
+ if self._appears_complete(content, target_file, original_content):
621
723
  break
622
724
  continuation_count += 1
623
725
 
624
- # Save continuation prompt for debugging
625
- continuation_prompt_file = os.path.join(debug_dir, f"{safe_filename}_continuation_{continuation_count}_prompt.txt")
626
- continuation_prompt = f"""
627
- You are "BioGuider," continuing a documentation generation task with enhanced capacity for complex documents.
628
-
629
- GOAL
630
- Continue generating the document "{target_file}" from where the previous generation left off.
631
- The previous content was truncated and needs to be completed. You now have increased token
632
- capacity to handle complex documents comprehensively.
633
-
634
- PREVIOUS CONTENT (do not repeat this):
635
- ```
636
- {content[-1000:]} # Last 1000 chars for context
637
- ```
638
-
639
- TASK
640
- Continue the document naturally from the last complete section. Maintain the same style,
641
- structure, and flow as the previous content. Complete all remaining sections that should
642
- be in this document.
643
-
644
- CRITICAL REQUIREMENTS:
645
- - Do NOT repeat any content already generated above
646
- - Do NOT duplicate sections, headers, or code blocks that already exist
647
- - Generate ONLY new, unique content that continues from where the previous content ended
648
- - If the previous content appears complete, add complementary sections that enhance the document
649
- - Focus on adding missing sections, examples, or explanations that weren't covered
650
-
651
- CAPACITY AND SCOPE
652
- - You have enhanced token capacity to handle complex documents comprehensively
653
- - Tutorial documents: Enhanced capacity for step-by-step content, code examples, and comprehensive explanations
654
- - Complex documents: Increased capacity for multiple sections, detailed explanations, and extensive content
655
- - Comprehensive documents: Full capacity for complete documentation with all necessary sections
656
-
657
- INPUTS
658
- - evaluation_report (contains {total_suggestions} suggestions to integrate): {json.dumps(evaluation_report)[:4000]}
659
- - context: {context[:2000]}
660
-
661
- REMINDER: SINGLE DOCUMENT APPROACH
662
- - The evaluation report contains {total_suggestions} SEPARATE suggestions
663
- - These should be integrated into ONE cohesive continuation
664
- - Do NOT create {total_suggestions} separate sections for each suggestion
665
- - Group related suggestions (e.g., setup, reproducibility, performance) and integrate them naturally
666
-
667
- REQUIREMENTS
668
- - Continue seamlessly from the previous content
669
- - Maintain the same tone and style
670
- - Complete all sections that should be in this document
671
- - Preserve file-specific formatting (e.g., YAML frontmatter, code block syntax appropriate to the language)
672
- - Do not repeat content already generated
673
- - Return only the continuation content, not the full document
674
- - Use the increased token capacity to provide thorough, complete content
675
- - NEVER invent technical specifications (hardware, versions, performance) unless explicitly in evaluation report or context
676
- - ABSOLUTELY FORBIDDEN: Do NOT wrap content in markdown code fences (```markdown). Return pure content only.
677
- - ABSOLUTELY FORBIDDEN: Do NOT add summary sections, notes, conclusions, or any text at the end of documents
678
-
679
- COMPLETENESS REQUIREMENTS
680
- - Generate complete, comprehensive content that addresses all remaining evaluation suggestions
681
- - For complex documents, ensure all sections are fully developed and detailed
682
- - For tutorial documents, include complete step-by-step instructions with examples
683
- - Use the increased token capacity to provide thorough, useful documentation
726
+ # Calculate total suggestions for debugging info
727
+ total_suggestions = 1
728
+ if isinstance(evaluation_report, dict):
729
+ if "total_suggestions" in evaluation_report:
730
+ total_suggestions = evaluation_report["total_suggestions"]
731
+ elif "suggestions" in evaluation_report and isinstance(evaluation_report["suggestions"], list):
732
+ total_suggestions = len(evaluation_report["suggestions"])
733
+
734
+ # Find better continuation point - look for last complete section
735
+ continuation_point = self._find_continuation_point(content, original_content)
736
+ if not continuation_point:
737
+ continuation_point = content[-1000:] # Fallback to last 1000 chars
684
738
 
685
- OUTPUT
686
- - Return only the continuation content. No commentary, no fences.
687
- """
739
+ # Generate continuation prompt using centralized template
740
+ continuation_prompt = LLM_CONTINUATION_PROMPT.format(
741
+ target_file=target_file,
742
+ existing_content_tail=continuation_point,
743
+ total_suggestions=total_suggestions,
744
+ evaluation_report_excerpt=json.dumps(evaluation_report)[:4000],
745
+ context_excerpt=context[:2000],
746
+ )
688
747
 
748
+ # Save continuation prompt for debugging
749
+ continuation_prompt_file = os.path.join(debug_dir, f"{safe_filename}_continuation_{continuation_count}_prompt.txt")
689
750
  with open(continuation_prompt_file, 'w', encoding='utf-8') as f:
690
751
  f.write(continuation_prompt)
691
752
 
@@ -768,4 +829,82 @@ OUTPUT
768
829
 
769
830
  return '\n'.join(cleaned_lines)
770
831
 
832
+ def _split_rmd_into_chunks(self, content: str) -> list[dict]:
833
+ chunks = []
834
+ if not content:
835
+ return chunks
836
+ lines = content.split('\n')
837
+ n = len(lines)
838
+ i = 0
839
+ if n >= 3 and lines[0].strip() == '---':
840
+ j = 1
841
+ while j < n and lines[j].strip() != '---':
842
+ j += 1
843
+ if j < n and lines[j].strip() == '---':
844
+ chunks.append({"type": "yaml", "content": '\n'.join(lines[0:j+1])})
845
+ i = j + 1
846
+ buffer = []
847
+ in_code = False
848
+ for k in range(i, n):
849
+ line = lines[k]
850
+ if line.strip().startswith('```'):
851
+ if in_code:
852
+ buffer.append(line)
853
+ chunks.append({"type": "code", "content": '\n'.join(buffer)})
854
+ buffer = []
855
+ in_code = False
856
+ else:
857
+ if buffer and any(s.strip() for s in buffer):
858
+ chunks.append({"type": "text", "content": '\n'.join(buffer)})
859
+ buffer = [line]
860
+ in_code = True
861
+ else:
862
+ buffer.append(line)
863
+ if buffer and any(s.strip() for s in buffer):
864
+ chunks.append({"type": "code" if in_code else "text", "content": '\n'.join(buffer)})
865
+ return chunks
866
+
867
+ def _generate_text_chunk(self, conv: CommonConversation, evaluation_report: dict, context: str, chunk_text: str) -> tuple[str, dict]:
868
+ LLM_CHUNK_PROMPT = (
869
+ "You are BioGuider improving a single markdown chunk of a larger RMarkdown document.\n\n"
870
+ "GOAL\nRefine ONLY the given chunk's prose per evaluation suggestions while preserving structure.\n"
871
+ "Do not add conclusions or new sections.\n\n"
872
+ "INPUTS\n- evaluation_report: <<{evaluation_report}>>\n- repo_context_excerpt: <<{context}>>\n- original_chunk:\n<<<\n{chunk}\n>>>\n\n"
873
+ "RULES\n- Preserve headers/formatting in this chunk.\n- Do not invent technical specs.\n- Output ONLY the refined chunk (no fences)."
874
+ )
875
+ system_prompt = LLM_CHUNK_PROMPT.format(
876
+ evaluation_report=json.dumps(evaluation_report)[:4000],
877
+ context=context[:1500],
878
+ chunk=chunk_text[:6000],
879
+ )
880
+ content, usage = conv.generate(system_prompt=system_prompt, instruction_prompt="Rewrite this chunk now.")
881
+ return content.strip(), usage
882
+
883
+ def _generate_full_document_chunked(self, target_file: str, evaluation_report: dict, context: str, original_content: str, debug_dir: str, safe_filename: str) -> tuple[str, dict]:
884
+ conv = CommonConversation(self.llm)
885
+ chunks = self._split_rmd_into_chunks(original_content)
886
+ merged = []
887
+ total_usage = {"total_tokens": 0, "prompt_tokens": 0, "completion_tokens": 0}
888
+ from datetime import datetime
889
+ for idx, ch in enumerate(chunks):
890
+ if ch["type"] in ("yaml", "code"):
891
+ merged.append(ch["content"])
892
+ continue
893
+ out, usage = self._generate_text_chunk(conv, evaluation_report, context, ch["content"])
894
+ if not out:
895
+ out = ch["content"]
896
+ merged.append(out)
897
+ try:
898
+ total_usage["total_tokens"] += int(usage.get("total_tokens", 0))
899
+ total_usage["prompt_tokens"] += int(usage.get("prompt_tokens", 0))
900
+ total_usage["completion_tokens"] += int(usage.get("completion_tokens", 0))
901
+ except Exception:
902
+ pass
903
+ chunk_file = os.path.join(debug_dir, f"{safe_filename}_chunk_{idx}.txt")
904
+ with open(chunk_file, 'w', encoding='utf-8') as f:
905
+ f.write(f"=== CHUNK {idx} ({ch['type']}) at {datetime.now().isoformat()} ===\n")
906
+ f.write(out)
907
+ content = '\n'.join(merged)
908
+ return content, total_usage
909
+
771
910
 
@@ -115,13 +115,9 @@ class LLMErrorInjector:
115
115
  max_words=max_words,
116
116
  )
117
117
  output, _ = conv.generate(system_prompt=system_prompt, instruction_prompt="Return the JSON now.")
118
- try:
119
- data = json.loads(output)
120
- except Exception:
121
- # try to locate JSON block
122
- start = output.find("{")
123
- end = output.rfind("}")
124
- data = json.loads(output[start:end+1]) if start != -1 and end != -1 else {"corrupted_markdown": readme_text, "errors": []}
118
+
119
+ # Enhanced JSON parsing with better error handling
120
+ data = self._parse_json_output(output, readme_text)
125
121
  corrupted = data.get("corrupted_markdown", readme_text)
126
122
  # Validate output stays within original context; fallback to deterministic if invalid
127
123
  if not self._validate_corrupted(readme_text, corrupted, preserve_keywords):
@@ -133,6 +129,63 @@ class LLMErrorInjector:
133
129
  }
134
130
  return corrupted, manifest
135
131
 
132
+ def _parse_json_output(self, output: str, fallback_text: str) -> Dict[str, Any]:
133
+ """Enhanced JSON parsing with multiple fallback strategies."""
134
+ import re
135
+
136
+ # Strategy 1: Direct JSON parsing
137
+ try:
138
+ return json.loads(output)
139
+ except json.JSONDecodeError:
140
+ pass
141
+
142
+ # Strategy 2: Extract JSON block between ```json and ```
143
+ json_pattern = r'```(?:json)?\s*(\{.*?\})\s*```'
144
+ match = re.search(json_pattern, output, re.DOTALL)
145
+ if match:
146
+ try:
147
+ return json.loads(match.group(1))
148
+ except json.JSONDecodeError:
149
+ pass
150
+
151
+ # Strategy 3: Find first complete JSON object
152
+ start = output.find("{")
153
+ if start != -1:
154
+ # Find matching closing brace
155
+ brace_count = 0
156
+ end = start
157
+ for i, char in enumerate(output[start:], start):
158
+ if char == "{":
159
+ brace_count += 1
160
+ elif char == "}":
161
+ brace_count -= 1
162
+ if brace_count == 0:
163
+ end = i
164
+ break
165
+
166
+ if brace_count == 0: # Found complete JSON object
167
+ try:
168
+ json_str = output[start:end+1]
169
+ return json.loads(json_str)
170
+ except json.JSONDecodeError:
171
+ pass
172
+
173
+ # Strategy 4: Try to fix common JSON issues
174
+ try:
175
+ # Remove markdown code fences
176
+ cleaned = re.sub(r'```(?:json)?\s*', '', output)
177
+ cleaned = re.sub(r'```\s*$', '', cleaned)
178
+ # Remove leading/trailing whitespace
179
+ cleaned = cleaned.strip()
180
+ # Try parsing again
181
+ return json.loads(cleaned)
182
+ except json.JSONDecodeError:
183
+ pass
184
+
185
+ # Strategy 5: Fallback to deterministic injection
186
+ print(f"Warning: Failed to parse LLM JSON output, using fallback. Output preview: {output[:200]}...")
187
+ return {"corrupted_markdown": fallback_text, "errors": []}
188
+
136
189
  def _extract_preserve_keywords(self, text: str) -> List[str]:
137
190
  # Extract capitalized terms, domain hyphenations, and hostnames in links
138
191
  kws: Set[str] = set()
@@ -21,7 +21,7 @@ class SuggestionExtractor:
21
21
  id=f"readme-dependencies-{file_name}",
22
22
  category="readme.dependencies",
23
23
  severity="should_fix",
24
- source={"section": "readme", "field": "dependency_suggestions", "evidence": dep_suggestions, "score": dep_score},
24
+ source={"section": "readme", "field": "dependency_suggestions", "score": dep_score},
25
25
  target_files=[file_name],
26
26
  action="add_dependencies_section",
27
27
  anchor_hint="Dependencies",
@@ -36,7 +36,7 @@ class SuggestionExtractor:
36
36
  id=f"readme-hardware-{file_name}",
37
37
  category="readme.hardware",
38
38
  severity="should_fix",
39
- source={"section": "readme", "field": "hardware_and_software_spec_suggestions", "evidence": hw_sw_suggestions, "score": hw_sw_score},
39
+ source={"section": "readme", "field": "hardware_and_software_spec_suggestions", "score": hw_sw_score},
40
40
  target_files=[file_name],
41
41
  action="add_system_requirements_section",
42
42
  anchor_hint="System Requirements",
@@ -51,7 +51,7 @@ class SuggestionExtractor:
51
51
  id=f"readme-purpose-{file_name}",
52
52
  category="readme.purpose",
53
53
  severity="should_fix",
54
- source={"section": "readme", "field": "project_purpose_suggestions", "evidence": purpose_suggestions, "score": purpose_score},
54
+ source={"section": "readme", "field": "project_purpose_suggestions", "score": purpose_score},
55
55
  target_files=[file_name],
56
56
  action="full_replace",
57
57
  anchor_hint="Overview",
@@ -66,7 +66,7 @@ class SuggestionExtractor:
66
66
  id=f"readme-readability-{file_name}",
67
67
  category="readme.readability",
68
68
  severity="should_fix",
69
- source={"section": "readme", "field": "readability_suggestions", "evidence": readability_suggestions, "score": readability_score},
69
+ source={"section": "readme", "field": "readability_suggestions", "score": readability_score},
70
70
  target_files=[file_name],
71
71
  action="full_replace",
72
72
  anchor_hint="Introduction",
@@ -78,7 +78,7 @@ class SuggestionExtractor:
78
78
  id=f"readme-intro-cleanup-{file_name}",
79
79
  category="readme.intro_cleanup",
80
80
  severity="should_fix",
81
- source={"section": "readme", "field": "overview", "evidence": "Improve top-level overview for clarity and tone.", "score": "Fair"},
81
+ source={"section": "readme", "field": "overview", "score": "Fair"},
82
82
  target_files=[file_name],
83
83
  action="replace_intro",
84
84
  anchor_hint="Overview",
@@ -92,7 +92,7 @@ class SuggestionExtractor:
92
92
  id=f"readme-dependencies-clarify-{file_name}",
93
93
  category="readme.dependencies",
94
94
  severity="should_fix",
95
- source={"section": "readme", "field": "dependencies", "evidence": str(dep_sugg), "score": dep_score},
95
+ source={"section": "readme", "field": "dependencies", "score": dep_score},
96
96
  target_files=[file_name],
97
97
  action="add_dependencies_section",
98
98
  anchor_hint="Dependencies",
@@ -103,7 +103,7 @@ class SuggestionExtractor:
103
103
  id=f"readme-dependencies-fallback-{file_name}",
104
104
  category="readme.dependencies",
105
105
  severity="should_fix",
106
- source={"section": "readme", "field": "dependencies", "evidence": f"score={dep_score}", "score": dep_score},
106
+ source={"section": "readme", "field": "dependencies", "score": dep_score},
107
107
  target_files=[file_name],
108
108
  action="add_dependencies_section",
109
109
  anchor_hint="Dependencies",
@@ -118,7 +118,7 @@ class SuggestionExtractor:
118
118
  id=f"readme-sysreq-clarify-{file_name}",
119
119
  category="readme.system_requirements",
120
120
  severity="should_fix",
121
- source={"section": "readme", "field": "hardware_and_software", "evidence": str(hw_sugg), "score": hw_score},
121
+ source={"section": "readme", "field": "hardware_and_software", "score": hw_score},
122
122
  target_files=[file_name],
123
123
  action="add_system_requirements_section",
124
124
  anchor_hint="System Requirements",
@@ -129,7 +129,7 @@ class SuggestionExtractor:
129
129
  id=f"readme-sysreq-fallback-{file_name}",
130
130
  category="readme.system_requirements",
131
131
  severity="should_fix",
132
- source={"section": "readme", "field": "hardware_and_software", "evidence": f"score={hw_score}", "score": hw_score},
132
+ source={"section": "readme", "field": "hardware_and_software", "score": hw_score},
133
133
  target_files=[file_name],
134
134
  action="add_system_requirements_section",
135
135
  anchor_hint="System Requirements",
@@ -144,7 +144,7 @@ class SuggestionExtractor:
144
144
  id=f"readme-license-{file_name}",
145
145
  category="readme.license",
146
146
  severity="nice_to_have",
147
- source={"section": "readme", "field": "license", "evidence": str(lic_sugg)},
147
+ source={"section": "readme", "field": "license"},
148
148
  target_files=[file_name],
149
149
  action="mention_license_section",
150
150
  anchor_hint="License",
@@ -159,7 +159,7 @@ class SuggestionExtractor:
159
159
  id=f"readme-structure-clarify-{file_name}",
160
160
  category="readme.readability",
161
161
  severity="should_fix",
162
- source={"section": "readability", "field": "readability_suggestions", "evidence": str(read_sugg), "score": read_score},
162
+ source={"section": "readability", "field": "readability_suggestions", "score": read_score},
163
163
  target_files=[file_name],
164
164
  action="normalize_headings_structure",
165
165
  anchor_hint="Installation",
@@ -170,7 +170,7 @@ class SuggestionExtractor:
170
170
  id=f"readme-structure-fallback-{file_name}",
171
171
  category="readme.readability",
172
172
  severity="should_fix",
173
- source={"section": "readability", "field": "readability_score", "evidence": f"score={read_score}", "score": read_score},
173
+ source={"section": "readability", "field": "readability_score", "score": read_score},
174
174
  target_files=[file_name],
175
175
  action="normalize_headings_structure",
176
176
  anchor_hint="Installation",
@@ -182,7 +182,7 @@ class SuggestionExtractor:
182
182
  id=f"readme-usage-{file_name}",
183
183
  category="readme.usage",
184
184
  severity="nice_to_have",
185
- source={"section": "readability", "field": "usage", "evidence": "Add Usage section as suggested."},
185
+ source={"section": "readability", "field": "usage"},
186
186
  target_files=[file_name],
187
187
  action="add_usage_section",
188
188
  anchor_hint="Usage",
@@ -208,7 +208,7 @@ class SuggestionExtractor:
208
208
  id=f"install-full-replace-{target}",
209
209
  category="installation.full_replace",
210
210
  severity="should_fix",
211
- source={"section": "installation", "field": "overall", "evidence": str(structured)},
211
+ source={"section": "installation", "field": "overall"},
212
212
  target_files=[target],
213
213
  action="full_replace",
214
214
  anchor_hint=None,
@@ -235,7 +235,7 @@ class SuggestionExtractor:
235
235
  id=f"userguide-readability-{file_name}-{i}",
236
236
  category="userguide.readability",
237
237
  severity="should_fix",
238
- source={"section": "userguide", "field": "readability_suggestions", "evidence": suggestion, "score": readability_score},
238
+ source={"section": "userguide", "field": "readability_suggestions", "score": readability_score},
239
239
  target_files=[file_name],
240
240
  action="full_replace",
241
241
  anchor_hint=f"Readability-{i+1}",
@@ -252,7 +252,7 @@ class SuggestionExtractor:
252
252
  id=f"userguide-context-{file_name}-{i}",
253
253
  category="userguide.context",
254
254
  severity="should_fix",
255
- source={"section": "userguide", "field": "context_and_purpose_suggestions", "evidence": suggestion, "score": context_score},
255
+ source={"section": "userguide", "field": "context_and_purpose_suggestions", "score": context_score},
256
256
  target_files=[file_name],
257
257
  action="full_replace",
258
258
  anchor_hint=f"Context-{i+1}",
@@ -269,7 +269,7 @@ class SuggestionExtractor:
269
269
  id=f"userguide-error-{file_name}-{i}",
270
270
  category="userguide.error_handling",
271
271
  severity="should_fix",
272
- source={"section": "userguide", "field": "error_handling_suggestions", "evidence": suggestion, "score": error_score},
272
+ source={"section": "userguide", "field": "error_handling_suggestions", "score": error_score},
273
273
  target_files=[file_name],
274
274
  action="full_replace",
275
275
  anchor_hint=f"Error-Handling-{i+1}",
@@ -284,7 +284,7 @@ class SuggestionExtractor:
284
284
  id=f"userguide-consistency-{file_name}",
285
285
  category="userguide.consistency",
286
286
  severity="should_fix",
287
- source={"section": "userguide", "field": "consistency", "evidence": f"score={score}"},
287
+ source={"section": "userguide", "field": "consistency", "score": score},
288
288
  target_files=[file_name],
289
289
  action="full_replace",
290
290
  anchor_hint="Examples",
@@ -309,7 +309,7 @@ class SuggestionExtractor:
309
309
  id=f"tutorial-readability-{file_name}-{i}",
310
310
  category="tutorial.readability",
311
311
  severity="should_fix",
312
- source={"section": "tutorial", "field": "readability_suggestions", "evidence": suggestion, "score": readability_score},
312
+ source={"section": "tutorial", "field": "readability_suggestions", "score": readability_score},
313
313
  target_files=[file_name],
314
314
  action="full_replace",
315
315
  anchor_hint="Introduction",
@@ -326,7 +326,7 @@ class SuggestionExtractor:
326
326
  id=f"tutorial-setup-{file_name}-{i}",
327
327
  category="tutorial.setup",
328
328
  severity="should_fix",
329
- source={"section": "tutorial", "field": "setup_and_dependencies_suggestions", "evidence": suggestion, "score": setup_score},
329
+ source={"section": "tutorial", "field": "setup_and_dependencies_suggestions", "score": setup_score},
330
330
  target_files=[file_name],
331
331
  action="full_replace",
332
332
  anchor_hint="Setup",
@@ -343,7 +343,7 @@ class SuggestionExtractor:
343
343
  id=f"tutorial-reproducibility-{file_name}-{i}",
344
344
  category="tutorial.reproducibility",
345
345
  severity="should_fix",
346
- source={"section": "tutorial", "field": "reproducibility_suggestions", "evidence": suggestion, "score": reproducibility_score},
346
+ source={"section": "tutorial", "field": "reproducibility_suggestions", "score": reproducibility_score},
347
347
  target_files=[file_name],
348
348
  action="full_replace",
349
349
  anchor_hint="Setup",
@@ -360,7 +360,7 @@ class SuggestionExtractor:
360
360
  id=f"tutorial-structure-{file_name}-{i}",
361
361
  category="tutorial.structure",
362
362
  severity="should_fix",
363
- source={"section": "tutorial", "field": "structure_and_navigation_suggestions", "evidence": suggestion, "score": structure_score},
363
+ source={"section": "tutorial", "field": "structure_and_navigation_suggestions", "score": structure_score},
364
364
  target_files=[file_name],
365
365
  action="full_replace",
366
366
  anchor_hint="Introduction",
@@ -377,7 +377,7 @@ class SuggestionExtractor:
377
377
  id=f"tutorial-code-{file_name}-{i}",
378
378
  category="tutorial.code_quality",
379
379
  severity="should_fix",
380
- source={"section": "tutorial", "field": "executable_code_quality_suggestions", "evidence": suggestion, "score": code_score},
380
+ source={"section": "tutorial", "field": "executable_code_quality_suggestions", "score": code_score},
381
381
  target_files=[file_name],
382
382
  action="full_replace",
383
383
  anchor_hint="Code Examples",
@@ -394,7 +394,7 @@ class SuggestionExtractor:
394
394
  id=f"tutorial-verification-{file_name}-{i}",
395
395
  category="tutorial.verification",
396
396
  severity="should_fix",
397
- source={"section": "tutorial", "field": "result_verification_suggestions", "evidence": suggestion, "score": verification_score},
397
+ source={"section": "tutorial", "field": "result_verification_suggestions", "score": verification_score},
398
398
  target_files=[file_name],
399
399
  action="full_replace",
400
400
  anchor_hint="Results",
@@ -411,7 +411,7 @@ class SuggestionExtractor:
411
411
  id=f"tutorial-performance-{file_name}-{i}",
412
412
  category="tutorial.performance",
413
413
  severity="should_fix",
414
- source={"section": "tutorial", "field": "performance_and_resource_notes_suggestions", "evidence": suggestion, "score": performance_score},
414
+ source={"section": "tutorial", "field": "performance_and_resource_notes_suggestions", "score": performance_score},
415
415
  target_files=[file_name],
416
416
  action="full_replace",
417
417
  anchor_hint="Performance",
@@ -424,7 +424,7 @@ class SuggestionExtractor:
424
424
  id=f"tutorial-consistency-{file_name}",
425
425
  category="tutorial.consistency",
426
426
  severity="should_fix",
427
- source={"section": "tutorial", "field": "consistency", "evidence": f"score={score}"},
427
+ source={"section": "tutorial", "field": "consistency", "score": score},
428
428
  target_files=[file_name],
429
429
  action="full_replace",
430
430
  anchor_hint=None,
@@ -208,8 +208,7 @@ class DocumentationGenerationManager:
208
208
  suggestions_list.append({
209
209
  "suggestion_number": idx,
210
210
  "category": s.category if hasattr(s, 'category') else "general",
211
- "content_guidance": s.content_guidance,
212
- "evidence": s.source.get("evidence", "") if s.source else ""
211
+ "content_guidance": s.content_guidance
213
212
  })
214
213
 
215
214
  merged_evaluation_report = {
@@ -233,11 +232,12 @@ class DocumentationGenerationManager:
233
232
  f.write(f"Merged into single call: YES\n")
234
233
  f.write(f"Suggestion IDs: {[s.id for s in file_suggestions]}\n\n")
235
234
 
236
- gen_content, gen_usage = self.llm_gen.generate_full_document(
237
- target_file=fpath,
238
- evaluation_report=merged_evaluation_report,
239
- context=original_content,
240
- )
235
+ gen_content, gen_usage = self.llm_gen.generate_full_document(
236
+ target_file=fpath,
237
+ evaluation_report=merged_evaluation_report,
238
+ context=original_content,
239
+ original_content=original_content,
240
+ )
241
241
 
242
242
  # Debug: Log completion
243
243
  with open(debug_log_file, 'a', encoding='utf-8') as f:
@@ -261,8 +261,9 @@ class DocumentationGenerationManager:
261
261
  self.print_step(step_name="GeneratingContent", step_output=f"Fallback: Generating full document for {e.suggestion_id} using LLM...")
262
262
  gen_content, gen_usage = self.llm_gen.generate_full_document(
263
263
  target_file=e.file_path,
264
- evaluation_report={"suggestion": suggestion.content_guidance, "evidence": suggestion.source.get("evidence", "") if suggestion.source else ""},
264
+ evaluation_report={"suggestion": suggestion.content_guidance},
265
265
  context=original_content,
266
+ original_content=original_content,
266
267
  )
267
268
  if isinstance(gen_content, str) and gen_content:
268
269
  self.print_step(step_name="LLMFullDoc", step_output=f"✓ Generated full document for {e.suggestion_id} ({gen_usage.get('total_tokens', 0)} tokens)")
@@ -495,7 +496,6 @@ class DocumentationGenerationManager:
495
496
  for e in edits:
496
497
  sug = next((s for s in suggestions if s.id == e.suggestion_id), None)
497
498
  guidance = sug.content_guidance if sug else ""
498
- evidence = sug.source.get("evidence", "") if sug and sug.source else ""
499
499
  section = e.anchor.get('value', 'General improvements')
500
500
 
501
501
  # Convert technical action names to user-friendly descriptions
@@ -567,61 +567,18 @@ class DocumentationGenerationManager:
567
567
 
568
568
  # Show evaluation reasoning that triggered this improvement
569
569
  if sug and sug.source:
570
- evidence = sug.source.get("evidence", "")
571
570
  score = sug.source.get("score", "")
572
571
  category = sug.category or ""
573
572
 
574
573
  # Format category for display (e.g., "readme.dependencies" -> "Dependencies")
575
574
  category_display = category.split('.')[-1].replace('_', ' ').title() if category else ""
576
575
 
577
- if evidence:
578
- # Handle different evidence types
579
- if isinstance(evidence, dict):
580
- # Extract key information from dict evidence
581
- evidence_text = evidence.get("dependency_suggestions", "") or evidence.get("evidence", "")
582
- if not evidence_text:
583
- evidence_text = f"Installation evaluation: {evidence.get('overall_score', 'Unknown')} score"
584
- else:
585
- evidence_text = str(evidence)
586
- # Handle Python dict string evidence (from full_replace actions)
587
- if evidence_text.startswith("{") and evidence_text.endswith("}"):
588
- try:
589
- import ast
590
- evidence_dict = ast.literal_eval(evidence_text)
591
- # Extract specific suggestions from the evaluation report
592
- dep_sugg = evidence_dict.get("dependency_suggestions", "")
593
- hw_req = evidence_dict.get("hardware_requirements", False)
594
- compat_os = evidence_dict.get("compatible_os", True)
595
- overall_score = evidence_dict.get("overall_score", "")
596
-
597
- # Build specific reason based on evaluation findings
598
- reasons = []
599
- if dep_sugg:
600
- reasons.append(f"Dependencies: {dep_sugg}")
601
- if hw_req is False:
602
- reasons.append("Hardware requirements not specified")
603
- if compat_os is False:
604
- reasons.append("Operating system compatibility unclear")
605
- if overall_score and overall_score not in ("Excellent", "Good"):
606
- reasons.append(f"Overall score: {overall_score}")
607
-
608
- if reasons:
609
- evidence_text = "; ".join(reasons)
610
- else:
611
- evidence_text = f"Installation evaluation score: {overall_score}"
612
- except:
613
- evidence_text = "Installation documentation needs improvement"
614
-
615
- if score and category_display:
616
- lines.append(f" - *Reason:* [{category_display} - {score}] {evidence_text}")
617
- elif score:
618
- lines.append(f" - *Reason:* [{score}] {evidence_text}")
619
- elif category_display:
620
- lines.append(f" - *Reason:* [{category_display}] {evidence_text}")
621
- else:
622
- lines.append(f" - *Reason:* {evidence_text}")
576
+ if score and category_display:
577
+ lines.append(f" - *Reason:* [{category_display} - {score}]")
623
578
  elif score:
624
- lines.append(f" - *Reason:* Evaluation score was '{score}' - needs improvement")
579
+ lines.append(f" - *Reason:* [{score}]")
580
+ elif category_display:
581
+ lines.append(f" - *Reason:* [{category_display}]")
625
582
 
626
583
  # Show what was actually implemented (different from reason)
627
584
  if guidance:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bioguider
3
- Version: 0.2.33
3
+ Version: 0.2.34
4
4
  Summary: An AI-Powered package to help biomedical developers to generate clear documentation
5
5
  License: MIT
6
6
  Author: Cankun Wang
@@ -46,17 +46,17 @@ bioguider/generation/__init__.py,sha256=esV02QgCsY67-HBwSHDbA5AcbKzNRIT3wDwwh6N4
46
46
  bioguider/generation/change_planner.py,sha256=0N10jvkfn2J9b598FKOKPQecwmQv68yeuUvMZn81nOI,9715
47
47
  bioguider/generation/document_renderer.py,sha256=Md8NMo0CXNIqatWOdKE-_4k02Y3T_BCLmEPLTEiYUCA,7984
48
48
  bioguider/generation/llm_cleaner.py,sha256=qFgS5xi7bBO8HAJ9WFNzH3p9AhOsAkYjchKQHuAUWWM,2917
49
- bioguider/generation/llm_content_generator.py,sha256=DEgk4uAgZrxBTVEN3ZuhL7W-tBfXOyn2X4e9rM1Gfhc,39748
50
- bioguider/generation/llm_injector.py,sha256=bVxP6Asv2em4MBOB5yFsS14AuaeT7NLKQQMcsEqXjPY,17352
49
+ bioguider/generation/llm_content_generator.py,sha256=iG8tDhZqzwu9NdJ2KrGdlHlC14u86nEe1mFxeZsWJMM,46696
50
+ bioguider/generation/llm_injector.py,sha256=MAT4wrjTiG0In94khfo23TqtF71iur6eQKQNH9SBCB8,19295
51
51
  bioguider/generation/models.py,sha256=MlJOLjPHk8xs-UGW-TGN_M9cevTuxTG4tjm1d1L15go,2699
52
52
  bioguider/generation/output_manager.py,sha256=uwLyavND4kXOHlsXB0Berab3y8u6bhaEmQOQLl7wDAM,1963
53
53
  bioguider/generation/repo_reader.py,sha256=ivTURU61fR8er4ev7gSpOxER3FJv2d9GAx_X5JoVTvQ,1177
54
54
  bioguider/generation/report_loader.py,sha256=bxajeTDxod36iFsbSZhXSQjotxqP7LuAg5MC9OqX_p0,5911
55
55
  bioguider/generation/style_analyzer.py,sha256=Vn9FAK1qJBNLolLC1tz362k4UBaPl107BlvkQc8pV2I,983
56
- bioguider/generation/suggestion_extractor.py,sha256=kkPOYE6FXRtYlogV0GQdBraZZJm08I6Oux5YKGUF1UU,29442
56
+ bioguider/generation/suggestion_extractor.py,sha256=UKQPUUEvbfJI-EuvH6O5DPRyRn8GaQMNvUWBkuuUhDk,28694
57
57
  bioguider/generation/test_metrics.py,sha256=ACXmSZc2L_UkkmC5h2s4tG44MXW1d-hClFwPCD5_BFI,7505
58
58
  bioguider/managers/evaluation_manager.py,sha256=7WlshfnqFkk34dDlf50qAINK5sFTaoCFE0f0vGYyRdc,5789
59
- bioguider/managers/generation_manager.py,sha256=m6hGu9_1HcL1YS0PMoFfXfVgDqsps1ahcj6L9E4jtoo,38636
59
+ bioguider/managers/generation_manager.py,sha256=2l44jG4iErug51YQ1eMKIDudb7uy84VocwR4zyuL410,35454
60
60
  bioguider/managers/generation_test_manager.py,sha256=3mOBzQVpsLo_LpSspJcofn3CNtvgagS1DMr9Zuwkzq4,5307
61
61
  bioguider/rag/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
62
62
  bioguider/rag/config.py,sha256=5g4IqTzgyfZfax9Af9CTkXShgItPOt4_9TEMSekCPik,4602
@@ -74,7 +74,7 @@ bioguider/utils/pyphen_utils.py,sha256=cdZc3qphkvMDeL5NiZ8Xou13M_uVNP7ifJ-FwxO-0
74
74
  bioguider/utils/python_file_handler.py,sha256=BERiE2RHxpu3gAzv26jr8ZQetkrtnMZOv9SjpQ7WIdg,2650
75
75
  bioguider/utils/r_file_handler.py,sha256=y57Y04wjgtFWve0lPg1EOrNNOccPfnNF0z2WnlFMX74,19616
76
76
  bioguider/utils/utils.py,sha256=h8OhCjzLpHkb3ndnjRBUOBHD7csbHdEVNXf75SRN8Zc,4413
77
- bioguider-0.2.33.dist-info/LICENSE,sha256=qzkvZcKwwA5DuSuhXMOm2LcO6BdEr4V7jwFZVL2-jL4,1065
78
- bioguider-0.2.33.dist-info/METADATA,sha256=Yyqyvrm_CLNHy1fgDluqbCsePUqA_mYwkzRupFTHbRU,1962
79
- bioguider-0.2.33.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
80
- bioguider-0.2.33.dist-info/RECORD,,
77
+ bioguider-0.2.34.dist-info/LICENSE,sha256=qzkvZcKwwA5DuSuhXMOm2LcO6BdEr4V7jwFZVL2-jL4,1065
78
+ bioguider-0.2.34.dist-info/METADATA,sha256=9uU3UneURALPzQ7WoE2w4HhEy8ALUHQ2SXBq-sds2p8,1962
79
+ bioguider-0.2.34.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
80
+ bioguider-0.2.34.dist-info/RECORD,,