ey-catalyst / business_continuity.py
bsny's picture
Update business_continuity.py
9242980 verified
# 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)}")
@business_continuity_router.post("/api/business-continuity/generate-recovery-strategies", response_model=RecoveryStrategiesResponse)
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()}")
@business_continuity_router.get("/api/business-continuity/health")
def health_check():
"""Health check endpoint"""
return {
"status": "healthy",
"groq_api_configured": bool(GROQ_API_KEY),
"timestamp": uuid.uuid4().hex
}