Spaces:
Running
Running
File size: 8,870 Bytes
00639e5 516fa71 00639e5 ca1791d 00639e5 ca1791d 00639e5 516fa71 00639e5 ca1791d 00639e5 ca1791d 00639e5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 | # src/report_generator.py
from typing import Dict, List
import json
SYSTEM_PROMPT = """You are a clinical AI assistant specialized in radiological reporting of kidney stones.
Your role is to generate preliminary AI-assisted reports based on automated detection results, grounded in provided clinical guidelines.
CRITICAL RULES:
1. GROUNDING: Only make clinical claims that are directly supported by the provided clinical guideline sources. Do not introduce medical information from your general knowledge.
2. CITATIONS: When making clinical recommendations or interpretations, reference the source guidelines (e.g., "per EAU Guidelines 2026" or "based on AUA recommendations").
3. CAUTIOUS LANGUAGE: Use appropriate radiological hedging:
- "Hyperdense focus suggestive of..." instead of "Stone is..."
- "Management options may include..." instead of "Treatment is..."
- "Morphology pattern suggests..." instead of "Shape is..."
4. UNCERTAINTY: Clearly state limitations:
- Size measurements are approximate (pixel-based estimation, assumed spacing 0.7 mm/px)
- Equivalent diameter is derived from segmentation mask area, not direct caliper measurement
- Density values are relative pixel intensities (0-255 PNG scale), not true Hounsfield Units
- Morphology metrics (circularity, solidity) are image-based approximations
- AI detection is not a substitute for radiologist interpretation
5. STRUCTURE: Follow the exact report format provided.
6. NO FABRICATION: If the provided sources do not cover a specific clinical question, acknowledge this rather than inventing information. In the REFERENCES section, list ONLY the exact source names shown in the [Source N] headers of the CLINICAL KNOWLEDGE CONTEXT. Do not add, modify, or invent any source names.
7. PROFESSIONAL TONE: Use formal clinical language appropriate for a radiology report.
8. COMPLETION: You MUST complete ALL 6 report sections without exception. Never end a response mid-sentence. If guideline context is insufficient for a section, write "Insufficient guideline context available" rather than truncating."""
REPORT_TEMPLATE = """Generate a clinical AI-assisted kidney stone detection report using the structure below.
=== DETECTION RESULTS (Automated Analysis) ===
{detection_json}
=== CLINICAL KNOWLEDGE CONTEXT (Retrieved from Guidelines) ===
{retrieved_context}
=== REPORT STRUCTURE ===
Generate the report with the following sections:
**1. FINDINGS**
- Number of detected hyperdense foci suggestive of urolithiasis
- For each stone: equivalent diameter (mm), estimated area (mm²), image quadrant location
- If multiple stones: total stone area and largest stone size
**2. IMAGE CHARACTERISTICS**
- For each stone: size category, shape category, relative density category
- Detection confidence score
- If morphology available: circularity score, solidity, eccentricity, orientation angle
- Density profile: mean pixel intensity, intensity homogeneity, contrast to surrounding tissue
**3. CLINICAL INTERPRETATION**
- Size-based assessment of spontaneous passage likelihood (reference EAU/AUA size thresholds)
- Shape-based passage prognosis: irregular or low-solidity stones may indicate increased obstruction risk
- Density homogeneity assessment: note relative density pattern (cautiously, as true HU not available)
- Reference specific guideline sources for each clinical claim
**4. MANAGEMENT CONSIDERATIONS**
- Treatment options based on size category, per retrieved guidelines
- Medical expulsive therapy (MET) eligibility if applicable
- Surgical options (ESWL/URS/PCNL) if applicable, noting morphology factors where relevant
- Always cite the source guideline
**5. LIMITATIONS & DISCLAIMERS**
- This is an AI-generated preliminary assessment only
- Size is derived from segmentation mask equivalent diameter (pixel-based, assumed 0.7 mm/px spacing)
- Density values are relative pixel intensities from PNG images, not true Hounsfield Units
- Morphology metrics are image-based approximations, not direct anatomical measurements
- Clinical correlation and radiologist review required
- Not a substitute for professional medical interpretation
**6. REFERENCES**
- List unique sources cited in the report
IMPORTANT: Complete all 6 sections in full before ending your response.
Generate the report now:"""
NO_STONE_PROMPT = """Generate a brief AI-assisted report stating that no kidney stones were detected in the analyzed image.
Include:
1. FINDINGS: State that no hyperdense foci suggestive of urolithiasis were identified
2. LIMITATIONS: AI detection has inherent false negative risk; clinical correlation needed
3. RECOMMENDATION: If clinical suspicion persists despite negative AI findings, further imaging or clinical evaluation is warranted
Keep the report concise but professional."""
def format_retrieved_context(retrieved_docs: List[Dict]) -> str:
"""Retrieved chunk'ları LLM için format'la."""
if not retrieved_docs:
return "No specific guideline context available."
MAX_CONTENT_CHARS = 900
formatted = []
for i, doc in enumerate(retrieved_docs, 1):
content = doc['content']
if len(content) > MAX_CONTENT_CHARS:
content = content[:MAX_CONTENT_CHARS] + "..."
entry = (
f"[Source {i}] {doc['source']} (Page {doc['page']})\n"
f"Relevance Score: {doc['similarity_score']:.3f}\n"
f"Content: {content}\n"
)
formatted.append(entry)
return "\n---\n".join(formatted)
def format_detection_features(features: Dict) -> str:
"""Detection/segmentation features'ı LLM için temiz JSON olarak format'la."""
clean_features = {
"stone_detected": features.get("stone_detected"),
"count": features.get("count"),
"multiple_stones": features.get("multiple_stones", False),
}
if features.get("largest_stone_mm"):
clean_features["largest_stone_mm"] = features["largest_stone_mm"]
if features.get("total_stone_area_mm2"):
clean_features["total_stone_area_mm2"] = features["total_stone_area_mm2"]
if features.get("stones"):
clean_features["stones"] = []
for stone in features["stones"]:
clean_stone = {
"stone_id": stone["stone_id"],
"size": stone["size"],
"location_quadrant": stone["location"]["quadrant"],
"shape": stone["shape"],
"density": stone["density"],
"detection_confidence": stone["detection_confidence"]
}
if stone.get("morphology"):
clean_stone["morphology"] = stone["morphology"]
clean_features["stones"].append(clean_stone)
return json.dumps(clean_features, indent=2)
def build_report_prompt(
features: Dict,
retrieved_docs: List[Dict]
) -> tuple[str, str]:
"""
Complete prompt'u inşa et.
Returns: (system_prompt, user_prompt) tuple
"""
if not features.get("stone_detected", False):
return SYSTEM_PROMPT, NO_STONE_PROMPT
detection_json = format_detection_features(features)
retrieved_context = format_retrieved_context(retrieved_docs)
user_prompt = REPORT_TEMPLATE.format(
detection_json=detection_json,
retrieved_context=retrieved_context
)
return SYSTEM_PROMPT, user_prompt
if __name__ == "__main__":
mock_features = {
"stone_detected": True,
"count": 1,
"stones": [{
"stone_id": 1,
"size": {
"equivalent_diameter_mm": 7.2,
"max_dimension_mm": 7.2,
"area_mm2": 40.7,
"size_category": "medium"
},
"location": {"quadrant": "upper-left"},
"shape": "irregular",
"density": {
"category": "high",
"mean_intensity": 210.4,
"std_intensity": 18.2,
"homogeneity": "homogeneous",
"contrast_to_surrounding": 45.3
},
"detection_confidence": 0.87,
"morphology": {
"area_px": 831.2,
"area_mm2": 40.7,
"perimeter_px": 112.4,
"circularity": 0.826,
"solidity": 0.91,
"eccentricity": 0.44,
"orientation_deg": 32.1
}
}],
"multiple_stones": False
}
mock_retrieved = [
{
"source": "EAU-Guidelines-2026.pdf",
"page": 17,
"similarity_score": 0.61,
"content": "α-blockers are recommended for 5-10mm distal ureter stones..."
}
]
system, user = build_report_prompt(mock_features, mock_retrieved)
print("=== SYSTEM PROMPT ===")
print(system)
print("\n=== USER PROMPT ===")
print(user)
|