Spaces:
Runtime error
Runtime error
| # business_continuity.py | |
| from fastapi import APIRouter, HTTPException | |
| from pydantic import BaseModel, Field | |
| from typing import List, Optional, Dict, Any | |
| import os | |
| import openai | |
| import json | |
| import uuid | |
| import logging | |
| import traceback | |
| import re | |
| from dotenv import load_dotenv | |
| # Configure logging | |
| logging.basicConfig( | |
| level=logging.INFO, | |
| format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' | |
| ) | |
| logger = logging.getLogger(__name__) | |
| # Load environment variables from .env file | |
| load_dotenv() | |
| # Environment Variables | |
| GROQ_API_KEY = os.environ.get("GROQ_API_KEY") | |
| if GROQ_API_KEY: | |
| GROQ_API_KEY = GROQ_API_KEY.strip() | |
| logger.info(f"GROQ_API_KEY loaded: {'Yes' if GROQ_API_KEY else 'No'}") | |
| # Request Models | |
| class BusinessProcess(BaseModel): | |
| department: str = Field(..., min_length=1, max_length=100) | |
| sub_department: Optional[str] = Field(default="", max_length=100) | |
| process_name: str = Field(..., min_length=1, max_length=200) | |
| process_description: str = Field(..., min_length=1, max_length=1000) | |
| class AnalysisData(BaseModel): | |
| impact_analysis: Optional[Dict[str, Any]] = Field(default_factory=dict) | |
| minimum_operating_requirements: Optional[Dict[str, Any]] = Field(default_factory=dict) | |
| class BusinessContinuityRequest(BaseModel): | |
| business_process: BusinessProcess | |
| analysis_data: AnalysisData = Field(default_factory=lambda: AnalysisData()) | |
| # Response Model | |
| class RecoveryStrategiesResponse(BaseModel): | |
| success: bool | |
| people_unavailability_strategy: str | |
| people_reasoning: str | |
| technology_data_unavailability_strategy: str | |
| technology_reasoning: str | |
| site_unavailability_strategy: str | |
| site_reasoning: str | |
| third_party_vendors_unavailability_strategy: str | |
| vendor_reasoning: str | |
| # Create router | |
| business_continuity_router = APIRouter() | |
| def call_groq_api(prompt: str, user_message: str): | |
| """Simple function to call GROQ API""" | |
| if not GROQ_API_KEY: | |
| logger.error("GROQ_API_KEY environment variable is not set") | |
| raise Exception("GROQ_API_KEY not configured") | |
| try: | |
| client = openai.OpenAI( | |
| api_key=GROQ_API_KEY, | |
| base_url="https://api.groq.com/openai/v1", | |
| timeout=60.0 | |
| ) | |
| response = client.chat.completions.create( | |
| model="llama3-8b-8192", | |
| messages=[ | |
| {"role": "system", "content": prompt}, | |
| {"role": "user", "content": user_message} | |
| ], | |
| temperature=0.1, | |
| max_tokens=1500 | |
| ) | |
| return response.choices[0].message.content | |
| except Exception as e: | |
| logger.error(f"API call failed: {str(e)}") | |
| raise Exception(f"API call failed: {str(e)}") | |
| def generate_recovery_strategies(request: BusinessContinuityRequest): | |
| """Generate business continuity recovery strategies""" | |
| request_id = str(uuid.uuid4()) | |
| logger.info(f"Request {request_id}: Starting for {request.business_process.process_name}") | |
| # Basic input validation | |
| if not request.business_process.process_name.strip(): | |
| raise HTTPException(status_code=400, detail="Process name cannot be empty") | |
| if not request.business_process.department.strip(): | |
| raise HTTPException(status_code=400, detail="Department cannot be empty") | |
| try: | |
| # Create system prompt with strong emphasis on valid JSON output | |
| system_prompt = """You are a business continuity expert. CRITICAL: You must ONLY output a valid, parsable JSON object and nothing else. No explanations, no formatting, just pure JSON. | |
| Your output MUST be in this EXACT format with NO DEVIATIONS: | |
| { | |
| "people_unavailability_strategy": "Strategy for when key staff are unavailable (2-3 sentences).", | |
| "people_reasoning": "Why this strategy works for this process (1 sentence).", | |
| "technology_data_unavailability_strategy": "Strategy for system failures (2-3 sentences).", | |
| "technology_reasoning": "Why this technology strategy works (1 sentence).", | |
| "site_unavailability_strategy": "Strategy for site unavailability (2-3 sentences).", | |
| "site_reasoning": "Why this site strategy works (1 sentence).", | |
| "third_party_vendors_unavailability_strategy": "Strategy for supplier disruptions (2-3 sentences).", | |
| "vendor_reasoning": "Why this vendor strategy works (1 sentence)." | |
| } | |
| CRITICAL: Your output MUST be a valid JSON object with the above structure. If you cannot generate this, return an error message in the same JSON format with a "message" field indicating the error. | |
| GIVE ONLY this JSON output. DO NOT include any other text, explanations, or formatting. | |
| THE ENTIRE CODE DEPENDS ON THIS JSON STRUCTURE BEING CORRECT. PLEASE ensure the JSON is valid and parsable. | |
| DO NOT add markdown code blocks, explanations, or any text before or after the JSON. ONLY output the raw JSON object.""" | |
| # Create user message - simplified | |
| user_message = f"""Generate business continuity strategies for: | |
| Process: {request.business_process.process_name} | |
| Department: {request.business_process.department} | |
| Description: {request.business_process.process_description}""" | |
| api_response = call_groq_api(system_prompt, user_message) | |
| logger.info(f"Request {request_id}: Received API response") | |
| # Parse JSON response directly | |
| try: | |
| # Simple direct parsing - trust the LLM to format correctly | |
| if not api_response.strip().endswith('}'): | |
| api_response += '}' | |
| strategies_data = json.loads(api_response) | |
| logger.info(f"Request {request_id}: Successfully parsed JSON") | |
| except json.JSONDecodeError as e: | |
| # If parsing fails, just return the error | |
| logger.error(f"Request {request_id}: JSON parsing failed - {str(e)}") | |
| logger.error(f"Request {request_id}: Raw response preview: {api_response}") | |
| # Safely get fields with fallbacks | |
| return RecoveryStrategiesResponse( | |
| success=True, | |
| people_unavailability_strategy=strategies_data.get("people_unavailability_strategy", "Strategy not generated"), | |
| people_reasoning=strategies_data.get("people_reasoning", "Reasoning not generated"), | |
| technology_data_unavailability_strategy=strategies_data.get("technology_data_unavailability_strategy", "Strategy not generated"), | |
| technology_reasoning=strategies_data.get("technology_reasoning", "Reasoning not generated"), | |
| site_unavailability_strategy=strategies_data.get("site_unavailability_strategy", "Strategy not generated"), | |
| site_reasoning=strategies_data.get("site_reasoning", "Reasoning not generated"), | |
| third_party_vendors_unavailability_strategy=strategies_data.get("third_party_vendors_unavailability_strategy", "Strategy not generated"), | |
| vendor_reasoning=strategies_data.get("vendor_reasoning", "Reasoning not generated"), | |
| message="Successfully generated recovery strategies", | |
| request_id=request_id | |
| ) | |
| except HTTPException: | |
| # Re-raise HTTP exceptions | |
| raise | |
| except Exception as e: | |
| logger.error(f"Request {request_id}: Unexpected error - {str(e)}") | |
| logger.error(f"Traceback: {traceback.format_exc()}") | |
| def health_check(): | |
| """Health check endpoint""" | |
| return { | |
| "status": "healthy", | |
| "groq_api_configured": bool(GROQ_API_KEY), | |
| "timestamp": uuid.uuid4().hex | |
| } |