diff --git a/README.md b/README.md index fa72240..2327590 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A FastAPI-based insurance AI assistant that integrates with talestorm-ai for cha ## Features - **Insurance Chat**: AI-powered chat interface for insurance-related questions -- **Insurance Estimation**: Comprehensive underwriting and pricing estimation +- **Insurance Estimation**: AI-powered estimation using TALESTORM API for comprehensive underwriting and pricing - **Integration**: Seamless integration with talestorm-ai API - **Modern API**: Built with FastAPI for high performance and automatic documentation @@ -43,7 +43,8 @@ OPENAI_API_KEY=your-openai-api-key-here # lolly-ai Configuration PUBLIC_LOLLY_PORT=7310 TALESTORM_API_KEY=your-talestorm-api-key-here -TALESTORM_AGENT_ID=your-talestorm-agent-id-here +TALESTORM_AGENT_ID=your-talestorm-chat-agent-id-here +TALESTORM_ESTIMATION_AGENT_ID=your-talestorm-estimation-agent-id-here INSURANCE_API_BASE_URL=https://apilolly.cyberpug.ru # Server Configuration @@ -90,6 +91,51 @@ To remove volumes as well: docker compose down -v ``` +## Agent Configuration + +The system uses two different TALESTORM agents for different purposes: + +- **TALESTORM_AGENT_ID**: Used for general insurance chat functionality +- **TALESTORM_ESTIMATION_AGENT_ID**: Used specifically for insurance estimation with structured output schema + +This separation allows for specialized agents optimized for their respective tasks. + +## Estimation Service + +The estimation service has been refactored to use the TALESTORM API instead of local underwriting logic. This provides: + +- **AI-Powered Analysis**: Uses advanced AI models for risk assessment and pricing +- **Comprehensive Evaluation**: Analyzes all health factors, medications, and conditions +- **Structured Output**: Returns detailed tier assignments and pricing for each applicant +- **Flexible Response**: Handles both accepted and rejected applications with detailed reasoning + +### Estimation Request Format + +The service accepts insurance application data and returns structured estimation results: + +```json +{ + "status": "accepted", + "details": { + "dtq": false, + "reason": "All applicants have manageable conditions...", + "tier": 3, + "total_price": 815.75 + }, + "results": [ + { + "name": "Sarah Johnson", + "applicant_type": "primary", + "age": 39, + "bmi": 23.38, + "tier": 3, + "rx_spend": 40.0, + "message": "Type 2 diabetes well-controlled..." + } + ] +} +``` + ## API Endpoints All API endpoints are versioned under `/api/v1/`: diff --git a/src/api/v1/router.py b/src/api/v1/router.py index 0680a6f..c27e63f 100644 --- a/src/api/v1/router.py +++ b/src/api/v1/router.py @@ -4,7 +4,7 @@ import httpx from . import models from ...services.chat_service import chat_service -from ...services.estimation_service import run_underwriting +from ...services.estimation_service import estimation_service from ...config import settings router = APIRouter() @@ -26,6 +26,7 @@ async def insurance_chat(request: models.InsuranceChatRequest): ) except Exception as e: + raise e raise HTTPException(status_code=500, detail=f"Error processing chat request: {str(e)}") @router.post("/estimation", response_model=models.EstimationResponse) @@ -39,104 +40,8 @@ async def estimate(request: models.EstimationRequest): detail="Missing required applicants or plans" ) - # Convert request to the format expected by run_underwriting - applicants_dict = [] - for applicant in request.applicants: - applicants_dict.append({ - "applicant": applicant.applicant, - "firstName": applicant.firstName, - "lastName": applicant.lastName, - "midName": applicant.midName, - "phone": applicant.phone, - "gender": applicant.gender, - "dob": applicant.dob.strftime("%d/%m/%Y"), - "nicotine": applicant.nicotine, - "weight": applicant.weight, - "heightFt": applicant.heightFt, - "heightIn": applicant.heightIn - }) - - phq_dict = { - "treatment": request.phq.treatment, - "invalid": request.phq.invalid, - "pregnancy": request.phq.pregnancy, - "effectiveDate": request.phq.effectiveDate.strftime("%d/%m/%Y"), - "disclaimer": request.phq.disclaimer, - "signature": request.phq.signature, - "medications": [ - { - "applicant": med.applicant, - "name": med.name, - "rxcui": med.rxcui, - "dosage": med.dosage, - "frequency": med.frequency, - "description": med.description - } for med in request.phq.medications - ], - "issues": [ - { - "key": issue.key, - "details": [ - { - "key": detail.key, - "description": detail.description - } for detail in issue.details - ] - } for issue in request.phq.issues - ], - "conditions": [ - { - "key": condition.key, - "description": condition.description - } for condition in request.phq.conditions - ] - } - - plans_dict = [ - { - "id": plan.id, - "coverage": plan.coverage, - "tier": plan.tier - } for plan in request.plans - ] - - # Step 1: Run estimation - underwriting_result = run_underwriting(applicants_dict, phq_dict, plans_dict) - - # Step 2: Check if DTQ → reject application - if underwriting_result["combined"].get("dtq"): - # For DTQ cases, call external reject API and return rejected status - if request.uid: - reject_response = await reject_application(request.uid) - return models.EstimationResponse( - status="rejected", - details=models.EstimationDetails( - dtq=True, - reason="Declined due to high-risk conditions (DTQ triggered).", - tier=int(underwriting_result["combined"]["tier"]), - total_price=underwriting_result["combined"]["total_price"] - ), - results=[ - models.EstimationResult( - name=result["name"], - applicant_type=result["applicant_type"], - age=result["age"] or 0, - bmi=result["bmi"] or 0.0, - tier=int(result["tier"]), - rx_spend=result["rx_spend"], - message=result["message"] - ) for result in underwriting_result["results"] - ] - ) - - # Step 3: Else → assign tier and submit to external API - final_tier = underwriting_result["combined"]["tier"] - plans = request.plans.copy() - if plans: - plans[0].tier = f"tier_{str(final_tier).replace('.', '_')}" - - # Assemble external payload - submission_payload = { + # Convert request to the format expected by the estimation service + request_data = { "applicants": [ { "applicant": applicant.applicant, @@ -157,7 +62,7 @@ async def estimate(request: models.EstimationRequest): "id": plan.id, "coverage": plan.coverage, "tier": plan.tier - } for plan in plans + } for plan in request.plans ], "phq": { "treatment": request.phq.treatment, @@ -204,27 +109,42 @@ async def estimate(request: models.EstimationRequest): } } - submit_response = await submit_application(submission_payload) + # Call the TALESTORM API for estimation + estimation_result = await estimation_service.estimate_insurance(request_data) + + # Handle the response from TALESTORM API + if estimation_result.get("status") == "error": + raise HTTPException( + status_code=500, + detail=estimation_result.get("details", {}).get("reason", "Estimation service error") + ) + + # Extract details and results from the TALESTORM response + details = estimation_result.get("details", {}) + results = estimation_result.get("results", []) + + # Convert results to the expected format + estimation_results = [] + for result in results: + estimation_results.append(models.EstimationResult( + name=result.get("name", "Unknown"), + applicant_type=result.get("applicant_type", "Unknown"), + age=result.get("age", 0), + bmi=result.get("bmi", 0.0), + tier=result.get("tier", 4), + rx_spend=result.get("rx_spend", 0.0), + message=result.get("message", "") + )) return models.EstimationResponse( - status="accepted", + status=estimation_result.get("status", "accepted"), details=models.EstimationDetails( - dtq=False, - reason=underwriting_result["combined"]["message"], - tier=int(underwriting_result["combined"]["tier"]), - total_price=underwriting_result["combined"]["total_price"] + dtq=details.get("dtq", False), + reason=details.get("reason", ""), + tier=details.get("tier", 4), + total_price=details.get("total_price", 0.0) ), - results=[ - models.EstimationResult( - name=result["name"], - applicant_type=result["applicant_type"], - age=result["age"] or 0, - bmi=result["bmi"] or 0.0, - tier=int(result["tier"]), - rx_spend=result["rx_spend"], - message=result["message"] - ) for result in underwriting_result["results"] - ] + results=estimation_results ) except HTTPException: @@ -236,23 +156,7 @@ async def estimate(request: models.EstimationRequest): ) -async def reject_application(uid: str) -> Dict[str, Any]: - """Reject application via external API""" - async with httpx.AsyncClient() as client: - response = await client.post( - f"{settings.INSURANCE_API_BASE_URL}/applications/reject", - json={"applicationId": uid} - ) - return response.json() if response.status_code == 200 else {"error": "Failed to reject application"} -async def submit_application(application_payload: Dict[str, Any]) -> Dict[str, Any]: - """Submit application via external API""" - async with httpx.AsyncClient() as client: - response = await client.post( - f"{settings.INSURANCE_API_BASE_URL}/applications/submit", - json=application_payload - ) - return response.json() if response.status_code == 200 else {"error": "Failed to submit application"} \ No newline at end of file diff --git a/src/config.py b/src/config.py index 61c434b..5b73bd8 100644 --- a/src/config.py +++ b/src/config.py @@ -12,6 +12,7 @@ class Settings(BaseSettings): TALESTORM_API_BASE_URL: str = "" TALESTORM_API_KEY: str TALESTORM_AGENT_ID: str + TALESTORM_ESTIMATION_AGENT_ID: str diff --git a/src/examples/test_agent_id.py b/src/examples/test_agent_id.py index 3785924..67555ba 100644 --- a/src/examples/test_agent_id.py +++ b/src/examples/test_agent_id.py @@ -17,7 +17,7 @@ async def test_agent_id_usage(): # Test session creation print("\nCreating session with configured agent...") - session_id = await session_service.create_session() + session_id = await session_service.create_session(agent_id=settings.TALESTORM_AGENT_ID) if session_id: print(f"✅ Session created: {session_id}") diff --git a/src/services/chat_service.py b/src/services/chat_service.py index dea098d..23a56d4 100644 --- a/src/services/chat_service.py +++ b/src/services/chat_service.py @@ -18,6 +18,7 @@ class ChatService: headers["X-API-Key"] = self.api_key return httpx.AsyncClient( + base_url=self.base_url, headers=headers ) @@ -25,7 +26,7 @@ class ChatService: """Send a message to talestorm-ai and get response""" async with await self.get_client() as client: try: - response = await client.post(self.base_url + "/chat/", json={ + response = await client.post("/chat/", json={ "chat_session_id": session_id, "user_message": message }) @@ -47,7 +48,7 @@ class ChatService: """Get chat history for a session and format it properly""" async with await self.get_client() as client: try: - response = await client.get(self.base_url + "/chat/", params={"chat_session_id": session_id}) + response = await client.get("/chat", params={"chat_session_id": session_id}) if response.status_code == 200: messages = response.json() history = [] @@ -101,10 +102,10 @@ class ChatService: """Process an insurance chat request""" try: if not session_id: - session_id = await session_service.create_session() + session_id = await session_service.create_session(agent_id=settings.TALESTORM_AGENT_ID) elif not await session_service.validate_session(session_id): - session_id = await session_service.create_session() + session_id = await session_service.create_session(agent_id=settings.TALESTORM_AGENT_ID) chat_response = await self.send_message(session_id, message) history = await self.get_chat_history(session_id) @@ -119,6 +120,7 @@ class ChatService: } except Exception as e: + raise e return { "session_id": session_id or "fallback-session", "answer": f"I'm sorry, I'm experiencing technical difficulties. Please try again later. Error: {str(e)}", diff --git a/src/services/estimation_service.py b/src/services/estimation_service.py index 77b9ad8..ca825a2 100644 --- a/src/services/estimation_service.py +++ b/src/services/estimation_service.py @@ -1,225 +1,228 @@ -from datetime import datetime +import httpx import json -from typing import List, Dict, Any, Optional +from typing import Dict, Any, List, Optional +from ..config import settings +from .session_service import session_service -# ---------------- Constants ---------------- # - -DECLINABLE_CONDITIONS = { - "aids", "organ_transplant", "multiple_sclerosis", "alzheimer_disease", - "heart_attack", "congestive_heart_failure", "bipolar", "seizures" -} - -DECLINABLE_ISSUE_KEYS = {"heart_attack", "congestive_heart_failure", "bipolar", "seizures"} -HIGH_RISK_FLAGS = {"50_units", "treat_diabetes"} - -PRICING_TABLE = { - 1: [122, 243, 219, 365], - 1.5: [127, 253, 228, 379], - 2: [133, 265, 239, 397], - 2.5: [139, 277, 249, 415], - 3: [145, 290, 261, 435], - 3.5: [151, 302, 272, 452], - 4: [158, 315, 283, 472], - 4.5: [164, 327, 295, 491], - 5: [172, 343, 309, 514], - 5.5: [182, 364, 327, 545], - 6: [196, 390, 351, 585], - 6.5: [209, 417, 376, 626], - 7: [222, 444, 400, 666] -} - -# Simplified drug price map (in a real implementation, you'd load this from a file) -DRUG_PRICE_MAP = { - "metformin": 15.0, - "insulin": 45.0, - "aspirin": 8.0, - "atorvastatin": 25.0, - "lisinopril": 12.0, - "amlodipine": 18.0, - "omeprazole": 22.0, - "simvastatin": 20.0, - "hydrochlorothiazide": 10.0, - "losartan": 30.0 -} - -# ---------------- Helper Functions ---------------- # - -def calculate_age(dob_str: str, effective_date_str: str) -> Optional[int]: - """Calculate age from date of birth and effective date""" - try: - dob = datetime.strptime(dob_str, "%d/%m/%Y") - ref = datetime.strptime(effective_date_str, "%d/%m/%Y") - return (ref - dob).days // 365 - except (ValueError, TypeError): - return None - -def calculate_bmi(weight: float, ft: int, inch: int) -> Optional[float]: - """Calculate BMI from weight and height""" - try: - inches = ft * 12 + inch - return (weight / (inches ** 2)) * 703 - except (ValueError, TypeError, ZeroDivisionError): - return None - -def check_declinable(phq: Dict[str, Any]) -> bool: - """Check if application should be declined based on PHQ data""" - if phq.get("treatment") or phq.get("invalid"): - return True +class EstimationService: + """Service for handling insurance estimation via TALESTORM API""" - for cond in phq.get("conditions", []): - if cond.get("key") in DECLINABLE_CONDITIONS: - return True + def __init__(self): + self.base_url = settings.TALESTORM_API_BASE_URL + self.api_key = settings.TALESTORM_API_KEY + self.agent_id = settings.TALESTORM_ESTIMATION_AGENT_ID - for issue in phq.get("issues", []): - for detail in issue.get("details", []): - if detail.get("key") in DECLINABLE_ISSUE_KEYS: - return True + async def get_client(self) -> httpx.AsyncClient: + """Get HTTP client for TALESTORM API""" + headers = { + "accept": "application/json", + "Content-Type": "application/json" + } + if self.api_key: + headers["X-API-Key"] = self.api_key + + return httpx.AsyncClient( + base_url=self.base_url, + headers=headers + ) - return False - -def base_tier(age: Optional[int]) -> float: - """Calculate base tier based on age""" - if age is None: - return 4.0 - if age <= 30: - return 1.0 - elif age <= 40: - return 2.0 - elif age <= 50: - return 3.0 - elif age <= 60: - return 3.5 - else: - return 4.0 - -def adjust_tier(tier: float, bmi: Optional[float], meds: List[Dict[str, Any]], - issues: List[Dict[str, Any]], applicant_index: int) -> float: - """Adjust tier based on health factors""" - if bmi is not None and (bmi < 16 or bmi > 40): - tier += 1.0 + def _format_estimation_request(self, request_data: Dict[str, Any]) -> str: + """Format the estimation request as a natural language message for the AI""" + applicants = request_data.get("applicants", []) + plans = request_data.get("plans", []) + phq = request_data.get("phq", {}) + income = request_data.get("income", 0) + address = request_data.get("address", {}) + + # Build the request message + message_parts = ["Please provide an insurance estimation for the following application. Analyze all health factors, medications, conditions, and risk factors to determine appropriate tier assignments and pricing:"] + + # Add applicants information + message_parts.append("\nApplicants:") + for i, applicant in enumerate(applicants): + applicant_type = "primary" if i == 0 else "dependent" if i > 0 else "unknown" + message_parts.append(f"- {applicant.get('firstName', '')} {applicant.get('lastName', '')} ({applicant_type})") + message_parts.append(f" Age: {applicant.get('dob', '')}") + message_parts.append(f" Gender: {applicant.get('gender', '')}") + message_parts.append(f" Weight: {applicant.get('weight', '')} lbs") + message_parts.append(f" Height: {applicant.get('heightFt', '')}'{applicant.get('heightIn', '')}\"") + message_parts.append(f" Nicotine use: {'Yes' if applicant.get('nicotine') else 'No'}") + + # Add plan information + if plans: + plan = plans[0] + message_parts.append(f"\nPlan: Coverage type {plan.get('coverage', '')}") + + # Add PHQ information + if phq: + message_parts.append("\nHealth Information:") + if phq.get("medications"): + message_parts.append("Medications:") + for med in phq["medications"]: + message_parts.append(f"- {med.get('name', '')} ({med.get('dosage', '')} {med.get('frequency', '')})") + + if phq.get("issues"): + message_parts.append("Health Issues:") + for issue in phq["issues"]: + message_parts.append(f"- {issue.get('key', '')}") + for detail in issue.get("details", []): + message_parts.append(f" * {detail.get('description', '')}") + + if phq.get("conditions"): + message_parts.append("Conditions:") + for condition in phq["conditions"]: + message_parts.append(f"- {condition.get('description', '')}") + + # Add income and address + message_parts.append(f"\nIncome: ${income:,.2f}") + if address: + addr_parts = [] + if address.get("address1"): + addr_parts.append(address["address1"]) + if address.get("address2"): + addr_parts.append(address["address2"]) + if address.get("city") and address.get("state"): + addr_parts.append(f"{address['city']}, {address['state']}") + if address.get("zipcode"): + addr_parts.append(address["zipcode"]) + + if addr_parts: + message_parts.append(f"Address: {', '.join(addr_parts)}") + + message_parts.append("\nPlease provide a detailed estimation including tier assignment, pricing, and individual applicant analysis.") + message_parts.append("\nConsider the following factors in your analysis:") + message_parts.append("- Age and BMI of each applicant") + message_parts.append("- Medical conditions and their severity") + message_parts.append("- Prescription medications and their costs") + message_parts.append("- Risk factors (nicotine use, etc.)") + message_parts.append("- Family coverage type and plan details") + message_parts.append("\nRespond with a JSON object that follows this exact schema:") + message_parts.append("{") + message_parts.append(' "status": "accepted" or "rejected",') + message_parts.append(' "details": {') + message_parts.append(' "dtq": boolean,') + message_parts.append(' "reason": "string",') + message_parts.append(' "tier": integer,') + message_parts.append(' "total_price": float') + message_parts.append(' },') + message_parts.append(' "results": [') + message_parts.append(' {') + message_parts.append(' "name": "string",') + message_parts.append(' "applicant_type": "string",') + message_parts.append(' "age": integer,') + message_parts.append(' "bmi": float,') + message_parts.append(' "tier": integer,') + message_parts.append(' "rx_spend": float,') + message_parts.append(' "message": "string"') + message_parts.append(' }') + message_parts.append(' ]') + message_parts.append('}') + + return "\n".join(message_parts) - personal_meds = [m for m in meds if m.get("applicant") == applicant_index] - if len(personal_meds) >= 3: - tier += 0.5 - - for issue in issues: - for detail in issue.get("details", []): - if detail.get("key") in HIGH_RISK_FLAGS: - tier += 0.5 - - return round(min(tier, 7.0) * 2) / 2 - -def calculate_rx_spend(medications: List[Dict[str, Any]], applicant_index: int) -> float: - """Calculate total prescription spend for an applicant""" - total = 0.0 - for med in medications: - if med.get("applicant") != applicant_index: - continue - drug = med.get("name", "").lower().strip() - if drug in DRUG_PRICE_MAP: - total += DRUG_PRICE_MAP[drug] - return round(total, 2) - -def get_rx_price(tier: float, coverage_type: int) -> float: - """Get prescription price based on tier and coverage type""" - tier = round(tier * 2) / 2 - coverage_index = { - 1: 0, 2: 1, 3: 2, 4: 3 - }.get(coverage_type, 0) - return PRICING_TABLE.get(tier, PRICING_TABLE[5])[coverage_index] - -# ---------------- Main Underwriting Logic ---------------- # - -def run_underwriting(applicants: List[Dict[str, Any]], phq: Dict[str, Any], - plans: List[Dict[str, Any]]) -> Dict[str, Any]: - """Run comprehensive underwriting analysis""" - results = [] - max_tier = 0 - dtq_found = False - ages = [] - - # Calculate ages for all applicants - for applicant in applicants: - age = calculate_age(applicant.get("dob"), phq.get("effectiveDate")) - if age is not None: - ages.append(age) - - oldest_age = max(ages) if ages else None - base = base_tier(oldest_age) if oldest_age else None - - coverage = plans[0]["coverage"] if plans else 1 - coverage_index = {1: 0, 2: 1, 3: 2, 4: 3}.get(coverage, 0) - - for idx, applicant in enumerate(applicants): + async def estimate_insurance(self, request_data: Dict[str, Any]) -> Dict[str, Any]: + """Send estimation request to TALESTORM API and parse response""" try: - age = calculate_age(applicant.get("dob"), phq.get("effectiveDate")) - bmi = calculate_bmi( - applicant.get("weight"), - applicant.get("heightFt"), - applicant.get("heightIn") - ) - name = applicant.get("firstName", "Unknown") - applicant_type = { - 1: "Primary", - 2: "Spouse", - 3: "Dependent" - }.get(applicant.get("applicant"), "Unknown") - - is_dtq = check_declinable(phq) - rx_spend = calculate_rx_spend(phq.get("medications", []), idx) - - if is_dtq: - tier = 5.0 - dtq_found = True - message = "Declined due to high-risk conditions (DTQ triggered)." - elif age is None or bmi is None or base is None: - tier = 4.5 - message = "Assigned fallback Tier 4.5 due to missing age or BMI." - else: - tier = base - tier = adjust_tier(tier, bmi, phq.get("medications", []), - phq.get("issues", []), idx) - - tier_price = PRICING_TABLE.get(tier, PRICING_TABLE[5])[coverage_index] - max_price = PRICING_TABLE[7][coverage_index] - - if rx_spend > max_price: - tier = 5.0 - message = f"Declined due to high Rx spend (${rx_spend} > ${max_price})" - elif rx_spend > tier_price: - for t in sorted(PRICING_TABLE.keys()): - if PRICING_TABLE[t][coverage_index] >= rx_spend: - tier = t - break - message = f"Rx spend ${rx_spend} increased tier to {tier}." - else: - message = f"Tier {tier} assigned with Rx spend ${rx_spend} within allowed limits." + # Create or get existing session with estimation agent + session_id = await session_service.create_session(agent_id=self.agent_id) + + # Format the request as a natural language message + estimation_message = self._format_estimation_request(request_data) + + # Send request to TALESTORM API + async with await self.get_client() as client: + response = await client.post( + "/chat/", + json={ + "chat_session_id": session_id, + "user_message": estimation_message + } + ) + + if response.status_code != 200: + raise Exception(f"TALESTORM API error: {response.status_code} {response.text}") + + # Parse the response + response_data = response.json() + message_content = response_data.get("message", "") + + # Try to parse the JSON response from the message + try: + # The response should contain a JSON string within the message + # First, try to extract JSON from the message if it's wrapped in code blocks + if "```json" in message_content: + start = message_content.find("```json") + 7 + end = message_content.find("```", start) + if end != -1: + message_content = message_content[start:end].strip() + elif "```" in message_content: + # Handle generic code blocks + start = message_content.find("```") + 3 + end = message_content.find("```", start) + if end != -1: + message_content = message_content[start:end].strip() + + # Clean up any leading/trailing whitespace and newlines + message_content = message_content.strip() + + parsed_response = json.loads(message_content) + + # Validate that the response has the expected structure + if not isinstance(parsed_response, dict): + raise ValueError("Response is not a JSON object") + + required_fields = ["status", "details", "results"] + for field in required_fields: + if field not in parsed_response: + raise ValueError(f"Missing required field: {field}") + + # Validate details structure + details = parsed_response.get("details", {}) + required_details = ["dtq", "reason", "tier", "total_price"] + for field in required_details: + if field not in details: + raise ValueError(f"Missing required detail field: {field}") + + # Validate results structure + results = parsed_response.get("results", []) + if not isinstance(results, list): + raise ValueError("Results must be an array") + + for result in results: + required_result_fields = ["name", "applicant_type", "age", "bmi", "tier", "rx_spend", "message"] + for field in required_result_fields: + if field not in result: + raise ValueError(f"Missing required result field: {field}") + + return parsed_response + + except (json.JSONDecodeError, ValueError) as e: + # If the response is not valid JSON or doesn't match schema, create a fallback response + print(f"Failed to parse JSON response: {e}") + print(f"Message content: {message_content}") + return { + "status": "error", + "details": { + "dtq": False, + "reason": f"Unable to parse AI response: {str(e)}", + "tier": 4, + "total_price": 0.0 + }, + "results": [] + } except Exception as e: - tier = 4.5 - rx_spend = 0.0 - message = f"Fallback Tier 4.5 due to system error: {str(e)}" + # Return error response + return { + "status": "error", + "details": { + "dtq": False, + "reason": f"Estimation service error: {str(e)}", + "tier": 4, + "total_price": 0.0 + }, + "results": [] + } - max_tier = max(max_tier, tier) - - results.append({ - "name": name, - "applicant_type": applicant_type, - "age": age, - "bmi": round(bmi, 2) if bmi else None, - "tier": tier, - "rx_spend": rx_spend, - "message": message - }) - - total_price = get_rx_price(max_tier, coverage) - - return { - "results": results, - "combined": { - "tier": max_tier, - "total_price": total_price, - "dtq": dtq_found, - "message": f"Final assigned tier is {max_tier} based on age, health, Rx cost, or fallback logic." - } - } \ No newline at end of file +# Global estimation service instance +estimation_service = EstimationService() \ No newline at end of file diff --git a/src/services/session_service.py b/src/services/session_service.py index 3c177ad..98e7448 100644 --- a/src/services/session_service.py +++ b/src/services/session_service.py @@ -26,7 +26,7 @@ class SessionService: """List available agents from talestorm-ai""" async with await self.get_client() as client: try: - response = await client.get("/agents/") + response = await client.get("/agents") if response.status_code == 200: return response.json() else: @@ -57,12 +57,11 @@ class SessionService: """Create a new chat session in talestorm-ai""" async with await self.get_client() as client: if not agent_id: - if self.agent_id: - agent_id = self.agent_id - else: - agent_id = settings.TALESTORM_AGENT_ID + # Use the default agent ID for backward compatibility + agent_id = self.agent_id or settings.TALESTORM_AGENT_ID response = await client.post("/sessions/", params={"agent_id": agent_id}) + print(response.request.url) session_data = response.json() return str(session_data["id"]) @@ -82,7 +81,7 @@ class SessionService: """List all sessions from talestorm-ai""" async with await self.get_client() as client: try: - response = await client.get("/sessions/") + response = await client.get("/sessions") if response.status_code == 200: return response.json() return []