Spaces:
Running
Running
| """ | |
| HBV Patient Assessment Module | |
| Evaluates patient eligibility for HBV treatment according to SASLT 2021 guidelines | |
| """ | |
| import logging | |
| import json | |
| import re | |
| from typing import Dict, Any | |
| from .config import get_llm | |
| logger = logging.getLogger(__name__) | |
| def clean_json_string(json_str: str) -> str: | |
| """ | |
| Clean a JSON string by properly escaping control characters within string values. | |
| This handles cases where LLMs generate JSON with literal newlines, tabs, etc. | |
| Args: | |
| json_str: Raw JSON string that may contain unescaped control characters | |
| Returns: | |
| Cleaned JSON string with properly escaped control characters | |
| """ | |
| # First, try to identify string values in the JSON and escape control characters within them | |
| # We need to be careful not to break the JSON structure itself | |
| # Replace common control characters that appear in string values | |
| # but preserve the JSON structure (newlines between key-value pairs are OK) | |
| # Strategy: Parse character by character, track if we're inside a string value | |
| result = [] | |
| in_string = False | |
| escape_next = False | |
| for i, char in enumerate(json_str): | |
| if escape_next: | |
| result.append(char) | |
| escape_next = False | |
| continue | |
| if char == '\\': | |
| result.append(char) | |
| escape_next = True | |
| continue | |
| if char == '"': | |
| in_string = not in_string | |
| result.append(char) | |
| continue | |
| # If we're inside a string value, escape control characters | |
| if in_string: | |
| if char == '\n': | |
| result.append('\\n') | |
| elif char == '\r': | |
| result.append('\\r') | |
| elif char == '\t': | |
| result.append('\\t') | |
| elif char == '\b': | |
| result.append('\\b') | |
| elif char == '\f': | |
| result.append('\\f') | |
| elif ord(char) < 32: # Other control characters | |
| result.append(f'\\u{ord(char):04x}') | |
| else: | |
| result.append(char) | |
| else: | |
| result.append(char) | |
| return ''.join(result) | |
| def normalize_recommendations(text: str) -> str: | |
| """ | |
| Normalize the recommendations string for concise, consistent formatting. | |
| - Remove extra blank lines | |
| - Trim trailing/leading whitespace on each line | |
| - Add single blank line before section headers for readability | |
| - Optionally cap length to avoid overly long outputs | |
| """ | |
| if not text: | |
| return "" | |
| # Normalize newlines | |
| text = text.replace('\r\n', '\n').replace('\r', '\n') | |
| # Split, strip, and drop empty lines | |
| lines = [ln.strip() for ln in text.split('\n')] | |
| lines = [ln for ln in lines if ln] | |
| # Add spacing before section headers (lines ending with ':') | |
| # but not before the first line | |
| formatted_lines = [] | |
| section_headers = [ | |
| 'Eligibility and Rationale:', | |
| 'Treatment Recommendations:', | |
| 'Monitoring and Follow-up:', | |
| 'Special Considerations:', | |
| 'References:' | |
| ] | |
| for i, line in enumerate(lines): | |
| # Add blank line before section headers (except first line) | |
| if i > 0 and any(line.startswith(header) for header in section_headers): | |
| formatted_lines.append('') # Add blank line | |
| formatted_lines.append(line) | |
| normalized = '\n'.join(formatted_lines) | |
| # Soft cap length (keep whole content if already short) | |
| max_len = 1800 | |
| if len(normalized) > max_len: | |
| normalized = normalized[:max_len].rstrip() | |
| return normalized | |
| # SASLT 2021 Guidelines - Hardcoded Page Contents | |
| SASLT_GUIDELINES = """ | |
| ===== SASLT 2021 GUIDELINES: TREATMENT & MANAGEMENT ===== | |
| ### 1. INITIATION OF TREATMENT [SASLT 2021, p. 6] | |
| • Treatment indications should also take into account patient's age, health status, risk of HBV transmission, family history of HCC or cirrhosis and extrahepatic manifestations [SASLT 2021, p. 6] | |
| • All patients with chronic hepatitis B (HBV DNA > 2,000 IU/mL, ALT > ULN), regardless of HBeAg status, and/or at least moderate liver necroinflammation or fibrosis (Grade A) [SASLT 2021, p. 6] | |
| • Patients with cirrhosis (compensated or decompensated), with any detectable HBV DNA level and regardless of ALT levels (Grade A) [SASLT 2021, p. 6] | |
| • Patients with HBV DNA > 20,000 IU/mL and ALT > 2xULN, regardless of the degree of fibrosis (Grade B) [SASLT 2021, p. 6] | |
| • Patients with HBeAg-positive chronic HBV infection (persistently normal ALT and high HBV DNA levels) may be treated if they are > 30 years, regardless of the severity of liver histological lesions (Grade D) [SASLT 2021, p. 6] | |
| • Patients with chronic HBV infection (HBV DNA > 2,000 IU/mL, ALT > ULN), regardless of HBeAg status, and a family history of HCC or cirrhosis and extrahepatic manifestations (Grade D) [SASLT 2021, p. 6] | |
| • Non‑cirrhotic patients should be considered for treatment if they have HBV DNA levels >2,000 IU/mL, serum ALT >~40 IU/L and severity of liver disease assessed by liver biopsy showing at least moderate necroinflammation and/or at least moderate fibrosis. | |
| Patients with HBV DNA greater than 20,000 IU/mL and ALT greater than 2x ULN can begin treatment without a liver biopsy. | |
| Patients with HBV DNA >2,000 IU/mL and at least moderate fibrosis may initiate treatment even if ALT levels are normal. | |
| ### 2. MANAGEMENT ALGORITHM [SASLT 2021, p. 6] | |
| • HBsAg positive with chronic HBV infection and no signs of chronic hepatitis → Monitor (HBsAg, HBeAg, HBV DNA, ALT, fibrosis assessment). Consider: risk of HCC, risk of HBV reactivation, extrahepatic manifestations, risk of HBV transmission [SASLT 2021, p. 6] | |
| • CHB (with/without cirrhosis) → Start antiviral treatment if indicated, otherwise return to monitoring [SASLT 2021, p. 6] | |
| • HBsAg negative, anti-HBc positive → No specialist follow-up (inform about HBV reactivation risk). In case of immunosuppression: start oral antiviral prophylaxis or monitor [SASLT 2021, p. 6] | |
| ### 3. MONITORING OF UNTREATED PATIENTS [SASLT 2021, p. 6-7] | |
| • Patients with HBeAg-positive chronic HBV infection who are younger than 30 years should be followed at least every 3-6 months (Grade B) [SASLT 2021, p. 7] | |
| • Patients with HBeAg-negative chronic HBV infection and serum HBV DNA <2,000 IU/ml should be followed every 6-12 months (Grade B) [SASLT 2021, p. 7] | |
| • Patients with HBeAg-negative chronic HBV infection and serum HBV DNA ≥2,000 IU/ml should be followed every 3 months for the first year and thereafter every 6 months (Grade D) [SASLT 2021, p. 7] | |
| ### 4. CHRONIC HEPATITIS B (CHB) TREATMENT [SASLT 2021, p. 7-8] | |
| • The treatment of choice is the long-term administration of a potent nucleos(t)ide analogue NA with a high barrier to resistance, regardless of the severity of liver disease (Grade A) [SASLT 2021, p. 8] | |
| • Preferred regimens are ETV, TDF and TAF as monotherapies (Grade A) [SASLT 2021, p. 8] | |
| • LAM, ADV and TBV are not recommended in the treatment of CHB (Grade A) [SASLT 2021, p. 8] | |
| • TAF has demonstrated superior renal and bone density safety profiles compared with TDF in head-to-head trials [SASLT 2021, p. 8] | |
| • International guidelines recommend switching individuals at high risk for bone or renal disease from TDF to either TAF or ETV [SASLT 2021, p. 8] | |
| • TAF maintains a better safety profile unless the patient's creatinine clearance (CrCl) is less than 15 mL/minute [SASLT 2021, p. 8] | |
| ### 5. HBV-HCV COINFECTION [SASLT 2021, p. 8-9] | |
| • Treatment of HCV through DAAs may lead to reactivation of HBV. Patients who meet the criteria for HBV treatment should be treated concurrently or before initiation of DAA (Grade A) [SASLT 2021, p. 9] | |
| • HBV DNA and ALT should be monitored every four to eight weeks while on DAA and three months after completion of therapy (Grade D) [SASLT 2021, p. 9] | |
| • ALT level should be monitored every four weeks while on DAA for patients who are HBsAg-negative but HBcAb-positive. If ALT starts to rise, HBsAg and HBV DNA must be obtained to determine the need to start HBV treatment (Grade D) [SASLT 2021, p. 9] | |
| ### 6. HBV-HDV COINFECTION [SASLT 2021, p. 9] | |
| • HDV is a defective virus that requires HBsAg to envelop its delta antigen, causing coinfection with HBV or superinfection in chronic HBV patients [SASLT 2021, p. 9] | |
| • Active HDV infection is defined by HDV IgM and RNA presence with unexplained LFT elevation [SASLT 2021, p. 9] | |
| • Treatment goal: Suppression of HDV replication [SASLT 2021, p. 9] | |
| • PEG-IFN for 1 year shows long-term benefits despite post-treatment viral relapse [SASLT 2021, p. 9] | |
| • NA monotherapy is ineffective against HDV replication [SASLT 2021, p. 9] | |
| ### 7. HBV-HIV COINFECTION [SASLT 2021, p. 9] | |
| • All HIV-positive patients with HBV co-infection should start ART irrespective of CD4 cell count (Grade A) [SASLT 2021, p. 9] | |
| • HBV-HIV co-infected patients should be treated with TDF- or TAF-based ART regimen (Grade A) [SASLT 2021, p. 9] | |
| ### 8. IMMUNOCOMPROMISED PATIENTS [SASLT 2021, p. 9] | |
| • Prophylaxis for all HBsAg-positive patients before chemotherapy or immunosuppressive therapy (Grade A) [SASLT 2021, p. 9] | |
| • HBsAg-negative/anti-HBc-positive patients need HBV prophylaxis if receiving anti-CD20 or stem cell transplantation [SASLT 2021, p. 9] | |
| • Continue prophylaxis for ≥6 months after immunosuppression (12 months for anti-CD20) [SASLT 2021, p. 9] | |
| • All patients undergoing immunosuppressive treatment or chemotherapy, even short‑term courses, should be screened for HBsAg, anti‑HBc, and anti‑HBs (and HBV DNA, if HBsAg is already positive). [SASLT 2021, p. 9] | |
| • We recommend prophylaxis for all patients with positive HBsAg before initiating chemotherapy or other immunosuppressive agents. [SASLT 2021, p. 9] | |
| • For HBsAg-negative and anti-HBc positive patients, we recommend HBV prophylaxis if they are candidates for anti CD20 or are undergoing stem cell transplantation. [SASLT 2021, p. 9] | |
| • We recommend starting HBV prophylaxis for HBsAg or anti‑HBc positive patients undergoing treatment with tumor necrosis factor (TNF) inhibitors. [SASLT 2021, p. 9] | |
| • We recommend HBV prophylaxis for all patients who are HBsAg or anti-HBc positive before initiation of immunotherapy such as anti‑programmed cell death (PD) ‑1 and anti‑programmed cell death‑ligand 1 (PD‑L1) therapy. [SASLT 2021, p. 9] | |
| ### 9. PREGNANCY [SASLT 2021, p. 9-10] | |
| • Screen all pregnant women for HBV in first trimester (Grade A) [SASLT 2021, p. 9] | |
| • HBV vaccine is safe in pregnancy for non-immune women without chronic HBV. [SASLT 2021, p. 9] | |
| • Treat pregnant women meeting standard therapy indications [SASLT 2021, p. 9] | |
| • Start antiviral prophylaxis with TDF (or TAF) for HBV DNA >100,000 IU/mL at 24-28 weeks (Grade D) [SASLT 2021, p. 10] | |
| • Switch to TDF/TAF if on ETV, ADV, or interferon during pregnancy (Grade D) [SASLT 2021, p. 10] | |
| • Delivery mode based on obstetric indications only [SASLT 2021, p. 10] | |
| • Breastfeeding permitted for HBsAg+ women on TDF (Grade B) [SASLT 2021, p. 10] | |
| ### KEY DEFINITIONS & CONTEXT | |
| * **ALT (Alanine Aminotransferase):** Normal range in Adults falls between 4-42 units/L (U/L). | |
| * **ULN (Upper Limit of Normal):** ULN means Upper Limit of Normal for ALT which is set at 40 units/L for the purpose of these guidelines. | |
| * ** Necroinflammatory activity scale; A1 = mild, A2 = moderate, A3 = severe | |
| Liver fibrosis is usually classified into five stages: | |
| F0—no fibrosis; F1—mild fibrosis, pericellular collagen deposits; F2—moderate fibrosis, beginning bridging fibrosis; F3—severe fibrosis, defined as presence of numerous bridges and septa; F4—cirrhosis | |
| """ | |
| def assess_hbv_eligibility(patient_data: Dict[str, Any]) -> Dict[str, Any]: | |
| """ | |
| Assess patient eligibility for HBV treatment based on SASLT 2021 guidelines | |
| using hardcoded guideline pages (3, 4, 6, 7, 8, 9, 10) and LLM analysis | |
| Args: | |
| patient_data: Dictionary containing patient clinical parameters | |
| Returns: | |
| Dictionary with assessment results: | |
| - eligible: bool | |
| - recommendations: str (comprehensive narrative with inline citations in format [SASLT 2021, Page X]) | |
| """ | |
| try: | |
| # # Check if HBsAg is positive (required for treatment consideration) | |
| # if patient_data.get("hbsag_status") != "Positive": | |
| # return { | |
| # "eligible": False, | |
| # "recommendations": "Patient is HBsAg negative. HBV treatment is not indicated. HBsAg positivity is required for HBV treatment consideration according to SASLT 2021 guidelines." | |
| # } | |
| # Use hardcoded SASLT 2021 guidelines instead of RAG retrieval | |
| logger.info("Using hardcoded SASLT 2021 guidelines (Pages 3, 4, 6, 7, 8, 9, 10)") | |
| # Define ALT ULN for context | |
| sex = patient_data.get("sex", "Male") | |
| #alt_uln = 35 if sex == "Male" else 25 | |
| # Format patient data for prompt | |
| age = patient_data.get("age", "N/A") | |
| pregnancy_status = patient_data.get("pregnancy_status", "N/A") | |
| hbsag_status = patient_data.get("hbsag_status", "N/A") | |
| duration_hbsag = patient_data.get("duration_hbsag_months", "N/A") | |
| hbv_dna = patient_data.get("hbv_dna_level", 0) | |
| hbeag_status = patient_data.get("hbeag_status", "N/A") | |
| alt_level = patient_data.get("alt_level", 0) | |
| fibrosis_stage = patient_data.get("fibrosis_stage", "N/A") | |
| necroinflammatory = patient_data.get("necroinflammatory_activity", "N/A") | |
| extrahepatic = patient_data.get("extrahepatic_manifestations", False) | |
| immunosuppression = patient_data.get("immunosuppression_status", "None") | |
| coinfections = patient_data.get("coinfections", []) | |
| family_history = patient_data.get("family_history_cirrhosis_hcc", False) | |
| comorbidities = patient_data.get("other_comorbidities", []) | |
| # Create prompt for LLM to analyze patient against guidelines | |
| analysis_prompt = f"""You are an HBV treatment eligibility assessment system. Analyze the patient data against the SASLT 2021 guidelines. | |
| PATIENT DATA: | |
| - Sex: {sex} | |
| - Age: {age} years | |
| - Pregnancy Status: {pregnancy_status} | |
| - HBsAg Status: {hbsag_status} | |
| - HBsAg Duration: {duration_hbsag} months | |
| - HBV DNA Level: {hbv_dna:,.0f} IU/mL | |
| - HBeAg Status: {hbeag_status} | |
| - ALT Level: {alt_level} | |
| - Fibrosis Stage: {fibrosis_stage} | |
| - Necroinflammatory Activity: {necroinflammatory} | |
| - Extrahepatic Manifestations: {extrahepatic} | |
| - Immunosuppression: {immunosuppression} | |
| - Coinfections: {', '.join(coinfections) if coinfections else 'None'} | |
| - Family History (Cirrhosis/HCC): {family_history} | |
| - Other Comorbidities: {', '.join(comorbidities) if comorbidities else 'None'} | |
| SASLT 2021 GUIDELINES (Retrieved Context): | |
| {SASLT_GUIDELINES} | |
| Based STRICTLY on the SASLT 2021 guidelines and criteria provided above, assess this patient's eligibility for HBV antiviral treatment. | |
| You MUST respond with a valid JSON object in this exact format: | |
| {{ | |
| "eligible": true or false, | |
| "recommendations": "Comprehensive assessment with inline citations" | |
| }} | |
| IMPORTANT JSON FORMATTING: | |
| - Return ONLY valid JSON without markdown code blocks. | |
| - You MUST use "\\n" to indicate line breaks inside the "recommendations" string and format the content as clear bullet lists prefixed with "- ". | |
| - Do NOT include literal newline characters. Use \\n for every new bullet or line. | |
| - Use SINGLE \\n between lines. Do NOT use \\n\\n (double newlines) anywhere. | |
| CRITICAL ELIGIBILITY HIERARCHY: | |
| 1. **Check Special Populations FIRST (Pages 9-10):** "Eligible" means eligible for ANY antiviral intervention (treatment OR prophylaxis). | |
| * If patient is **HBsAg-positive** AND **Immunosuppressed** (Page 9): Set **"eligible": true"** (for prophylaxis). | |
| * If patient has **HBV-HIV coinfection** (Page 9): Set **"eligible": true"** (for ART). | |
| * If patient is **Pregnant** with **HBV DNA > 100,000 IU/mL** (Page 10): Set **"eligible": true"** (for prophylaxis). | |
| * If patient has **HBV-HCV coinfection** AND meets HBV treatment criteria (Page 9): Set **"eligible": true"**. | |
| 2. **Check Standard Criteria SECOND (Page 6):** | |
| * If *not* eligible based on special populations, check standard criteria (HBV DNA, ALT, fibrosis, age, family history). | |
| * If any Page 6 criteria are met (e.g., Cirrhosis, or HBV DNA > 2,000 + ALT > ULN + moderate fibrosis, etc.): Set **"eligible": true"**. | |
| 3. **If NO criteria from (1) or (2) are met:** Set **"eligible": false"**. | |
| 4. The "eligible" flag MUST be consistent with the "Eligibility and Rationale" bullet. Do not contradict yourself. | |
| STRUCTURE AND CONTENT OF "recommendations" (CONCISE & ORGANIZED): | |
| - Use ONLY these sections in this exact order, each as a header followed by 1-3 concise bullets: | |
| 1. "Eligibility and Rationale:" (1-2 bullets max, MUST state the primary reason for eligibility/ineligibility) | |
| 2. "Treatment Recommendations:" (1-3 bullets: first-line drugs if eligible, or "Treatment not indicated" if not eligible) | |
| 3. "Monitoring and Follow-up:" (1-2 bullets) | |
| 4. "Special Considerations:" (0-2 bullets, ONLY if applicable and *not* the primary reason for eligibility) | |
| 5. "References:" (1 line listing pages cited) | |
| - OMIT "Additional Notes" and any other sections. | |
| - Keep each bullet to ONE sentence (max 25 words per bullet). | |
| - Total output: aim for 8-12 bullets maximum across all sections. | |
| BULLETING AND CITATIONS RULES: | |
| - Put citations at the end of each bullet using "[SASLT 2021, Page X]". | |
| - Include evidence grade when available (e.g., "(Grade A)"). | |
| - Only cite pages 6–10 that actually contain the information. | |
| STRICT ACCURACY AND CONSISTENCY RULES: | |
| - **NO CONTRADICTIONS:** The "eligible" flag MUST match the rationale. Follow the CRITICAL ELIGIBILITY HIERARCHY. | |
| - **Rationale First:** The Rationale MUST state the primary reason. If eligible due to immunosuppression, state that, even if Page 6 criteria are not met. | |
| - **Use ONLY the provided SASLT 2021 content;** do NOT add external knowledge. | |
| - **BREVITY:** Each bullet = 1 sentence, max 25 words. Total = 8-12 bullets max. | |
| PAGE-TO-TOPIC MAPPING GUIDANCE (for correct citations): | |
| - Page 6: Standard initiation of treatment criteria, management algorithm. | |
| - Page 7: Monitoring of untreated patients, CHB treatment principles. | |
| - Page 8: Treatment drugs/regimens (ETV, TDF, TAF), agents not recommended. | |
| - Page 9: Special populations (HBV-HCV, HBV-HDV, HBV-HIV, Immunocompromised). | |
| - Page 10: Pregnancy-related recommendations. | |
| --- | |
| EXAMPLE OUTPUT (ELIGIBLE - STANDARD CRITERIA) - Use SINGLE \\n only: | |
| {{ | |
| "eligible": true, | |
| "recommendations": "Eligibility and Rationale:\\n- Eligible: HBV DNA > 2,000 IU/mL, ALT > ULN, moderate fibrosis (Grade A) [SASLT 2021, Page 6]\\nTreatment Recommendations:\\n- Start monotherapy with ETV, TDF, or TAF (Grade A) [SASLT 2021, Page 8]\\nMonitoring and Follow-up:\\n- Monitor treatment response per SASLT protocol [SASLT 2021, Page 7]\\nReferences:\\n- Pages 6, 7, 8: Treatment criteria, drugs, monitoring" | |
| }} | |
| --- | |
| EXAMPLE OUTPUT (NOT ELIGIBLE - STANDARD CRITERIA) - Use SINGLE \\n only: | |
| {{ | |
| "eligible": false, | |
| "recommendations": "Eligibility and Rationale:\\n- Not eligible: HBV DNA < 2,000 IU/mL, ALT ≤ ULN, no significant fibrosis [SASLT 2021, Page 6]\\nTreatment Recommendations:\\n- Treatment not indicated at this time [SASLT 2021, Page 6]\\nMonitoring and Follow-up:\\n- Monitor every 6-12 months (HBeAg-negative, HBV DNA < 2,000 IU/mL) (Grade B) [SASLT 2021, Page 7]\\nReferences:\\n- Pages 6, 7: Treatment criteria, monitoring protocols" | |
| }} | |
| --- | |
| EXAMPLE OUTPUT (ELIGIBLE - IMMUNOSUPPRESSION) - Use SINGLE \\n only: | |
| {{ | |
| "eligible": true, | |
| "recommendations": "Eligibility and Rationale:\\n- Eligible: HBsAg-positive patient requires prophylaxis for immunosuppressive therapy (Grade A) [SASLT 2021, Page 9]\\nTreatment Recommendations:\\n- Start antiviral prophylaxis (e.g., ETV, TDF, TAF) [SASLT 2021, Page 8, 9]\\nMonitoring and Follow-up:\\n- Continue prophylaxis for ≥6 months after immunosuppression (12 for anti-CD20) [SASLT 2021, Page 9]\\nReferences:\\n- Pages 8, 9: Prophylaxis criteria, drug options, monitoring" | |
| }} | |
| --- | |
| EXAMPLE OUTPUT (ELIGIBLE - HIV COINFECTION) - Use SINGLE \\n only: | |
| {{ | |
| "eligible": true, | |
| "recommendations": "Eligibility and Rationale:\\n- Eligible: Patient has HBV-HIV coinfection and should start ART (Grade A) [SASLT 2021, Page 9]\\nTreatment Recommendations:\\n- ART regimen must include TDF- or TAF-based therapy (Grade A) [SASLT 2021, Page 9]\\nMonitoring and Follow-up:\\n- Monitor patient closely after ART initiation for immune reconstitution [SASLT 2021, Page 9]\\nReferences:\\n- Page 9: HBV-HIV coinfection management, ART regimens" | |
| }} | |
| --- | |
| CRITICAL REQUIREMENTS: | |
| 1. Base assessment ONLY on SASLT 2021 guidelines provided. | |
| 2. Follow the CRITICAL ELIGIBILITY HIERARCHY to avoid contradictions. | |
| 3. Keep output SHORT: 8-12 bullets total, 1 sentence per bullet (max 25 words). | |
| 4. Use ONLY the 5 sections listed above. | |
| 5. Cite exact page at end of each bullet: [SASLT 2021, Page X]. | |
| 6. Return ONLY valid JSON, no markdown, no extra text. | |
| """ | |
| # Log the complete prompt being sent to LLM | |
| logger.info(f"\n{'='*80}") | |
| logger.info(f"LLM PROMPT") | |
| logger.info(f"{'='*80}") | |
| logger.info(f"\n{analysis_prompt}\n") | |
| logger.info(f"{'='*80}\n") | |
| # Get LLM response | |
| llm = get_llm() | |
| logger.info("Sending prompt to LLM...") | |
| response = llm.invoke(analysis_prompt) | |
| logger.info("LLM response received") | |
| # Extract JSON from response | |
| response_text = response.content if hasattr(response, 'content') else str(response) | |
| # Log LLM response | |
| logger.info(f"\n{'='*80}") | |
| logger.info(f"LLM RESPONSE") | |
| logger.info(f"{'='*80}") | |
| logger.info(f"\n{response_text}\n") | |
| logger.info(f"{'='*80}\n") | |
| # Try to parse JSON from response | |
| try: | |
| # Remove markdown code blocks if present | |
| if '```json' in response_text: | |
| json_start = response_text.find('```json') + 7 | |
| json_end = response_text.find('```', json_start) | |
| response_text = response_text[json_start:json_end].strip() | |
| elif '```' in response_text: | |
| json_start = response_text.find('```') + 3 | |
| json_end = response_text.find('```', json_start) | |
| response_text = response_text[json_start:json_end].strip() | |
| # Find JSON in response (handle cases where LLM adds extra text) | |
| json_start = response_text.find('{') | |
| json_end = response_text.rfind('}') + 1 | |
| if json_start >= 0 and json_end > json_start: | |
| json_str = response_text[json_start:json_end] | |
| # Clean the JSON string to escape control characters within string values | |
| cleaned_json_str = clean_json_string(json_str) | |
| logger.debug(f"Cleaned JSON string (first 500 chars): {cleaned_json_str[:500]}") | |
| # Parse the cleaned JSON | |
| result = json.loads(cleaned_json_str) | |
| logger.info(f"✅ Successfully parsed JSON response") | |
| else: | |
| raise ValueError("No JSON found in response") | |
| # Validate and return result | |
| normalized_recs = normalize_recommendations(result.get("recommendations", "")) | |
| assessment_result = { | |
| "eligible": result.get("eligible", False), | |
| "recommendations": normalized_recs | |
| } | |
| # Log final assessment | |
| logger.info(f"\n{'='*80}") | |
| logger.info(f"FINAL ASSESSMENT") | |
| logger.info(f"{'='*80}") | |
| logger.info(f"Eligible: {assessment_result['eligible']}") | |
| logger.info(f"Recommendations length: {len(assessment_result['recommendations'])} characters") | |
| logger.info(f"{'='*80}\n") | |
| return assessment_result | |
| except (json.JSONDecodeError, ValueError) as e: | |
| logger.error(f"Failed to parse LLM response as JSON: {e}") | |
| logger.error(f"Response text: {response_text}") | |
| # Fallback: return error response | |
| return { | |
| "eligible": False, | |
| "recommendations": f"Error parsing assessment results. Please try again. Error details: {str(e)}" | |
| } | |
| except Exception as e: | |
| logger.error(f"Error in assess_hbv_eligibility: {str(e)}") | |
| raise |