Yaswanth-Bolla commited on
Commit
1f51659
·
1 Parent(s): 7a26ec6

Fixed the fallback issue

Browse files
Files changed (1) hide show
  1. business_continuity.py +312 -155
business_continuity.py CHANGED
@@ -1,50 +1,109 @@
1
  # business_continuity.py
2
  from fastapi import APIRouter, HTTPException
3
- from pydantic import BaseModel
4
  from typing import List, Optional, Dict, Any
5
  import os
6
  import openai
7
  import json
8
  import uuid
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
  # Environment Variables
11
  GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
12
  if GROQ_API_KEY:
13
  GROQ_API_KEY = GROQ_API_KEY.strip()
14
 
15
- # Model Setup
16
- def generate_response(system_prompt: str, user_message: str):
 
 
 
 
 
 
17
  if not GROQ_API_KEY:
 
18
  raise Exception("GROQ_API_KEY environment variable is not set")
19
 
20
- client = openai.OpenAI(api_key=GROQ_API_KEY, base_url="https://api.groq.com/openai/v1")
21
- try:
22
- response = client.chat.completions.create(
23
- model="llama3-8b-8192",
24
- messages=[
25
- {"role": "system", "content": system_prompt},
26
- {"role": "user", "content": user_message}
27
- ],
28
- temperature=0.4
29
- )
30
- return response.choices[0].message.content
31
- except Exception as e:
32
- raise Exception(f"GROQ API connection failed: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
- # Request Models
35
  class BusinessProcess(BaseModel):
36
- department: str
37
- sub_department: Optional[str] = ""
38
- process_name: str
39
- process_description: str
40
 
41
  class AnalysisData(BaseModel):
42
- impact_analysis: Optional[Dict[str, Any]] = {}
43
- minimum_operating_requirements: Optional[Dict[str, Any]] = {}
44
 
45
  class BusinessContinuityRequest(BaseModel):
46
  business_process: BusinessProcess
47
- analysis_data: AnalysisData
48
 
49
  # Response Models
50
  class RecoveryStrategiesResponse(BaseModel):
@@ -58,6 +117,125 @@ class RecoveryStrategiesResponse(BaseModel):
58
  third_party_vendors_unavailability_strategy: str
59
  vendor_reasoning: str
60
  message: str
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
  # Business Continuity Router
63
  business_continuity_router = APIRouter()
@@ -67,150 +245,129 @@ def generate_recovery_strategies(request: BusinessContinuityRequest):
67
  """
68
  Generate comprehensive business continuity recovery strategies for a business process
69
  """
70
- system_prompt = """You are a business continuity expert with extensive experience in developing recovery strategies for various business processes. Your task is to generate comprehensive, actionable recovery strategies and their reasoning for specific business processes.
71
-
72
- CRITICAL: You must respond with ONLY a valid JSON object. Do not include any markdown formatting, code blocks, or additional text.
73
-
74
- For each recovery strategy, you need to:
75
- 1. Provide detailed, actionable steps that can be immediately implemented
76
- 2. Consider the specific nature of the business process and its dependencies
77
- 3. Include realistic timelines and resource requirements
78
- 4. Reference industry best practices and standards (ISO 22301, NIST, COOP guidelines)
79
- 5. Provide clear reasoning that explains why each strategy is appropriate for the specific process
80
-
81
- Consider these factors:
82
- - Business process criticality and dependencies
83
- - Resource requirements (human, technology, facilities)
84
- - Regulatory and compliance considerations
85
- - Cost-effectiveness and feasibility
86
- - Recovery time objectives (RTO) and recovery point objectives (RPO)
87
- - Industry-specific risks and challenges
88
- - Stakeholder communication requirements
89
- - Testing and maintenance needs
90
-
91
- Provide specific justifications that reference:
92
- - Industry standards and best practices
93
- - Historical incident data and lessons learned
94
- - Process-specific vulnerabilities and requirements
95
- - Resource availability and constraints
96
- - Regulatory requirements and compliance needs
97
- - Business impact considerations
98
- - Technology dependencies and alternatives
99
-
100
- RESPOND WITH ONLY THIS EXACT JSON FORMAT (no markdown, no code blocks, no additional text):
101
  {
102
- "people_unavailability_strategy": "Detailed strategy for handling personnel unavailability with specific actionable steps",
103
- "people_reasoning": "Clear explanation of why this strategy is appropriate for this specific business process",
104
- "technology_data_unavailability_strategy": "Detailed strategy for handling technology and data unavailability with specific steps",
105
- "technology_reasoning": "Clear explanation of why this technology strategy fits this process",
106
- "site_unavailability_strategy": "Detailed strategy for handling site unavailability with actionable steps",
107
- "site_reasoning": "Clear explanation of why this site strategy is suitable for this process",
108
- "third_party_vendors_unavailability_strategy": "Detailed strategy for handling vendor/supplier disruptions with specific steps",
109
- "vendor_reasoning": "Clear explanation of why this vendor strategy is appropriate for this process"
110
  }"""
111
 
112
- # Convert request to JSON for the prompt
113
- input_json = {
114
- "business_process": {
115
- "department": request.business_process.department,
116
- "sub_department": request.business_process.sub_department,
117
- "process_name": request.business_process.process_name,
118
- "process_description": request.business_process.process_description
119
- },
120
- "analysis_data": {
121
- "impact_analysis": request.analysis_data.impact_analysis,
122
- "minimum_operating_requirements": request.analysis_data.minimum_operating_requirements
123
- }
124
- }
125
-
126
- user_message = f"""You are a business continuity expert. Generate comprehensive recovery strategies AND their reasoning for the business process described in the following JSON data:
127
-
128
- {json.dumps(input_json, indent=2)}
129
-
130
- Please analyze the provided business process information and provide specific, actionable recovery strategies for each of the following scenarios. Each strategy should be detailed, practical, and specific to this business process.
131
-
132
- For EACH strategy, also provide a clear reasoning explanation of WHY this strategy was recommended for this specific process.
133
-
134
- Strategy Categories Required:
135
- 1. People Unavailability Strategy - What to do when key personnel are unavailable
136
- 2. Technology/Data Unavailability Strategy - How to handle IT system failures or data loss
137
- 3. Site Unavailability Strategy - Plans for when the primary work location is inaccessible
138
- 4. Third Party Vendors Unavailability Strategy - Contingencies for vendor/supplier disruptions
139
-
140
- Output Requirements:
141
- - Format your response as a valid JSON object with these exact keys:
142
- - people_unavailability_strategy
143
- - people_reasoning
144
- - technology_data_unavailability_strategy
145
- - technology_reasoning
146
- - site_unavailability_strategy
147
- - site_reasoning
148
- - third_party_vendors_unavailability_strategy
149
- - vendor_reasoning
150
-
151
- - Each strategy should be a detailed string (2-3 sentences minimum) with actionable steps
152
- - Each reasoning should explain WHY this strategy was chosen for this specific process (1-2 sentences)
153
- - Return ONLY the JSON object, no additional text or formatting"""
154
 
155
- try:
156
- result = generate_response(system_prompt, user_message)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
 
158
- # Clean the response - remove markdown code blocks if present
159
- cleaned_result = result.strip()
160
- if cleaned_result.startswith('```json'):
161
- cleaned_result = cleaned_result[7:] # Remove ```json
162
- elif cleaned_result.startswith('```'):
163
- cleaned_result = cleaned_result[3:] # Remove ```
164
- if cleaned_result.endswith('```'):
165
- cleaned_result = cleaned_result[:-3] # Remove trailing ```
166
- cleaned_result = cleaned_result.strip()
167
 
168
- # Extract JSON from the response
169
- json_start = cleaned_result.find('{')
170
- json_end = cleaned_result.rfind('}') + 1
171
 
172
- if json_start != -1 and json_end > json_start:
173
- json_str = cleaned_result[json_start:json_end]
174
- strategies_data = json.loads(json_str)
 
 
 
 
175
 
176
- return RecoveryStrategiesResponse(
177
- success=True,
178
- people_unavailability_strategy=strategies_data.get("people_unavailability_strategy", ""),
179
- people_reasoning=strategies_data.get("people_reasoning", ""),
180
- technology_data_unavailability_strategy=strategies_data.get("technology_data_unavailability_strategy", ""),
181
- technology_reasoning=strategies_data.get("technology_reasoning", ""),
182
- site_unavailability_strategy=strategies_data.get("site_unavailability_strategy", ""),
183
- site_reasoning=strategies_data.get("site_reasoning", ""),
184
- third_party_vendors_unavailability_strategy=strategies_data.get("third_party_vendors_unavailability_strategy", ""),
185
- vendor_reasoning=strategies_data.get("vendor_reasoning", ""),
186
- message="Successfully generated comprehensive recovery strategies"
187
- )
188
- else:
189
- raise ValueError("No valid JSON found in response")
190
 
191
- except (json.JSONDecodeError, ValueError) as e:
192
- # Fallback response with generic but relevant strategies
193
- process_name = request.business_process.process_name
194
- department = request.business_process.department
195
 
196
- fallback_response = RecoveryStrategiesResponse(
197
- success=True,
198
- people_unavailability_strategy=f"Develop cross-training programs for key roles in {process_name}, maintain updated contact lists for temporary staff, and establish clear escalation procedures for critical decision-making. Document all critical processes to ensure knowledge transfer and implement a buddy system for essential personnel.",
199
- people_reasoning=f"Cross-training and documentation are essential for {process_name} in {department} to reduce single points of failure and ensure continuity when key personnel are unavailable.",
200
 
201
- technology_data_unavailability_strategy=f"Implement regular automated data backups for {process_name} systems, establish redundant systems and failover procedures, and maintain updated disaster recovery documentation. Test backup systems monthly and ensure all critical data is replicated to secure off-site locations.",
202
- technology_reasoning=f"Data protection and system redundancy are critical for {process_name} operations to minimize downtime and ensure rapid recovery from technology failures.",
 
 
 
 
 
 
 
 
 
 
 
203
 
204
- site_unavailability_strategy=f"Identify and pre-approve alternative work locations for {process_name} operations, enable remote work capabilities with necessary tools and secure access, and establish communication protocols for distributed teams. Maintain emergency supplies and equipment at alternate locations.",
205
- site_reasoning=f"Alternative work arrangements ensure {process_name} operations can continue even when primary {department} facilities are inaccessible due to emergencies or disasters.",
206
 
207
- third_party_vendors_unavailability_strategy=f"Maintain relationships with alternate vendors and suppliers critical to {process_name}, keep emergency contact information updated, and develop contingency contracts for essential services. Regularly assess vendor stability and maintain inventory buffers for critical supplies.",
208
- vendor_reasoning=f"Vendor diversification and contingency planning reduce dependency risks for {process_name} and ensure continuity of critical external services and supplies.",
 
209
 
210
- message="Generated fallback recovery strategies due to processing error"
211
- )
 
212
 
213
- return fallback_response
214
-
215
  except Exception as e:
216
- raise HTTPException(status_code=500, detail=f"Error generating recovery strategies: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # business_continuity.py
2
  from fastapi import APIRouter, HTTPException
3
+ from pydantic import BaseModel, Field
4
  from typing import List, Optional, Dict, Any
5
  import os
6
  import openai
7
  import json
8
  import uuid
9
+ import logging
10
+ import traceback
11
+ import re
12
+ from dotenv import load_dotenv
13
+
14
+ # Configure logging
15
+ logging.basicConfig(
16
+ level=logging.INFO,
17
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
18
+ )
19
+ logger = logging.getLogger(__name__)
20
+
21
+ # Load environment variables from .env file
22
+ load_dotenv()
23
 
24
  # Environment Variables
25
  GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
26
  if GROQ_API_KEY:
27
  GROQ_API_KEY = GROQ_API_KEY.strip()
28
 
29
+ logger.info(f"GROQ_API_KEY loaded: {'Yes' if GROQ_API_KEY else 'No'}")
30
+ if GROQ_API_KEY:
31
+ logger.info(f"GROQ_API_KEY preview: {GROQ_API_KEY[:10]}...")
32
+
33
+ # Model Setup with improved error handling
34
+ def generate_response(system_prompt: str, user_message: str, max_retries: int = 3):
35
+ """Generate response from GROQ API with retry logic and comprehensive error handling"""
36
+
37
  if not GROQ_API_KEY:
38
+ logger.error("GROQ_API_KEY environment variable is not set")
39
  raise Exception("GROQ_API_KEY environment variable is not set")
40
 
41
+ logger.info("Initializing GROQ API client")
42
+
43
+ for attempt in range(max_retries):
44
+ try:
45
+ logger.info(f"Attempt {attempt + 1}/{max_retries} - Making API call to GROQ")
46
+
47
+ client = openai.OpenAI(
48
+ api_key=GROQ_API_KEY,
49
+ base_url="https://api.groq.com/openai/v1",
50
+ timeout=60.0 # Increased timeout
51
+ )
52
+
53
+ response = client.chat.completions.create(
54
+ model="llama3-8b-8192",
55
+ messages=[
56
+ {"role": "system", "content": system_prompt},
57
+ {"role": "user", "content": user_message}
58
+ ],
59
+ temperature=0.2, # Even lower for more consistent responses
60
+ max_tokens=3000, # Increased further to avoid truncation
61
+ top_p=0.9,
62
+ frequency_penalty=0.0,
63
+ presence_penalty=0.0
64
+ )
65
+
66
+ content = response.choices[0].message.content
67
+ logger.info(f"API call successful - Response length: {len(content) if content else 0}")
68
+
69
+ if not content or content.strip() == "":
70
+ logger.warning(f"Empty response on attempt {attempt + 1}")
71
+ if attempt == max_retries - 1:
72
+ raise Exception("Received empty response from GROQ API after all retries")
73
+ continue
74
+
75
+ return content
76
+
77
+ except openai.APITimeoutError as e:
78
+ logger.warning(f"Timeout error on attempt {attempt + 1}: {str(e)}")
79
+ if attempt == max_retries - 1:
80
+ raise Exception(f"GROQ API timeout after {max_retries} attempts: {str(e)}")
81
+
82
+ except openai.APIError as e:
83
+ logger.error(f"GROQ API error on attempt {attempt + 1}: {str(e)}")
84
+ if attempt == max_retries - 1:
85
+ raise Exception(f"GROQ API error after {max_retries} attempts: {str(e)}")
86
+
87
+ except Exception as e:
88
+ logger.error(f"Unexpected error on attempt {attempt + 1}: {str(e)}")
89
+ logger.error(f"Traceback: {traceback.format_exc()}")
90
+ if attempt == max_retries - 1:
91
+ raise Exception(f"GROQ API connection failed after {max_retries} attempts: {str(e)}")
92
 
93
+ # Request Models with validation
94
  class BusinessProcess(BaseModel):
95
+ department: str = Field(..., min_length=1, max_length=100)
96
+ sub_department: Optional[str] = Field(default="", max_length=100)
97
+ process_name: str = Field(..., min_length=1, max_length=200)
98
+ process_description: str = Field(..., min_length=1, max_length=1000)
99
 
100
  class AnalysisData(BaseModel):
101
+ impact_analysis: Optional[Dict[str, Any]] = Field(default_factory=dict)
102
+ minimum_operating_requirements: Optional[Dict[str, Any]] = Field(default_factory=dict)
103
 
104
  class BusinessContinuityRequest(BaseModel):
105
  business_process: BusinessProcess
106
+ analysis_data: AnalysisData = Field(default_factory=lambda: AnalysisData())
107
 
108
  # Response Models
109
  class RecoveryStrategiesResponse(BaseModel):
 
117
  third_party_vendors_unavailability_strategy: str
118
  vendor_reasoning: str
119
  message: str
120
+ request_id: str = Field(default_factory=lambda: str(uuid.uuid4()))
121
+
122
+ def clean_json_response(response_text: str) -> str:
123
+ """Clean and extract JSON from API response"""
124
+ logger.info("Cleaning JSON response")
125
+
126
+ if not response_text:
127
+ logger.error("Empty response text provided")
128
+ raise ValueError("Empty response text")
129
+
130
+ # Remove common markdown formatting
131
+ cleaned = response_text.strip()
132
+
133
+ # Remove code blocks more aggressively
134
+ patterns = [
135
+ r'```json\s*', # Matches opening fence with optional spaces after ```json
136
+ r'```', # Matches closing or generic triple backticks
137
+ ]
138
+ for pattern in patterns:
139
+ cleaned = re.sub(pattern, '', cleaned, flags=re.MULTILINE | re.DOTALL)
140
+
141
+ cleaned = cleaned.strip()
142
+
143
+ # Try to find complete JSON by counting braces
144
+ json_start = cleaned.find('{')
145
+ if json_start == -1:
146
+ logger.error(f"No opening brace found. Response preview: {cleaned[:300]}...")
147
+ raise ValueError("No JSON opening brace found in response")
148
+
149
+ # Count braces to find the matching closing brace
150
+ brace_count = 0
151
+ json_end = -1
152
+
153
+ for i in range(json_start, len(cleaned)):
154
+ if cleaned[i] == '{':
155
+ brace_count += 1
156
+ elif cleaned[i] == '}':
157
+ brace_count -= 1
158
+ if brace_count == 0:
159
+ json_end = i
160
+ break
161
+
162
+ if json_end == -1:
163
+ logger.error(f"No matching closing brace found. Response preview: {cleaned[:500]}...")
164
+ # Try using the last closing brace as fallback
165
+ json_end = cleaned.rfind('}')
166
+ if json_end == -1 or json_end <= json_start:
167
+ raise ValueError("No valid JSON structure found in response")
168
+
169
+ json_str = cleaned[json_start:json_end + 1]
170
+ logger.info(f"Extracted JSON length: {len(json_str)}")
171
+ logger.info(f"JSON preview: {json_str[:150]}...")
172
+ logger.info(f"JSON ending: ...{json_str[-50:]}")
173
+
174
+ # Validate that it's at least syntactically correct JSON
175
+ try:
176
+ json.loads(json_str)
177
+ logger.info("JSON syntax validation passed")
178
+ except json.JSONDecodeError as e:
179
+ logger.error(f"JSON syntax validation failed: {str(e)}")
180
+ logger.error(f"Problematic JSON: {json_str}")
181
+ raise ValueError(f"Invalid JSON syntax: {str(e)}")
182
+
183
+ return json_str
184
+
185
+
186
+ def validate_strategies_data(data: dict) -> dict:
187
+ """Validate and clean strategies data"""
188
+ logger.info("Validating strategies data")
189
+
190
+ required_fields = [
191
+ "people_unavailability_strategy",
192
+ "people_reasoning",
193
+ "technology_data_unavailability_strategy",
194
+ "technology_reasoning",
195
+ "site_unavailability_strategy",
196
+ "site_reasoning",
197
+ "third_party_vendors_unavailability_strategy",
198
+ "vendor_reasoning"
199
+ ]
200
+
201
+ # Ensure all required fields exist and are strings
202
+ for field in required_fields:
203
+ if field not in data:
204
+ logger.warning(f"Missing field: {field}")
205
+ data[field] = "Strategy not generated - please retry request"
206
+ elif not isinstance(data[field], str):
207
+ logger.warning(f"Invalid type for field {field}: {type(data[field])}")
208
+ data[field] = str(data[field]) if data[field] is not None else "Strategy not generated"
209
+ elif len(data[field].strip()) == 0:
210
+ logger.warning(f"Empty field: {field}")
211
+ data[field] = "Strategy not generated - please retry request"
212
+
213
+ logger.info("Data validation completed")
214
+ return data
215
+
216
+ def generate_fallback_response(request: BusinessContinuityRequest, error_msg: str = "") -> RecoveryStrategiesResponse:
217
+ """Generate fallback response when API fails"""
218
+ logger.info("Generating fallback response")
219
+
220
+ process_name = request.business_process.process_name
221
+ department = request.business_process.department
222
+
223
+ return RecoveryStrategiesResponse(
224
+ success=True,
225
+ people_unavailability_strategy=f"Implement cross-training programs for critical roles in {process_name}, maintain updated emergency contact lists, and establish clear succession planning. Create detailed process documentation and implement job rotation to reduce single points of failure.",
226
+ people_reasoning=f"Cross-training and succession planning are essential for {process_name} in {department} to ensure continuity when key personnel are unavailable.",
227
+
228
+ technology_data_unavailability_strategy=f"Establish automated backup systems for {process_name} data, implement redundant infrastructure with failover capabilities, and maintain disaster recovery procedures. Conduct regular backup testing and ensure secure off-site data replication.",
229
+ technology_reasoning=f"Robust backup and redundancy systems are critical for {process_name} to minimize downtime and ensure rapid recovery from technology failures.",
230
+
231
+ site_unavailability_strategy=f"Identify and prepare alternative work locations for {process_name} operations, enable comprehensive remote work capabilities, and establish emergency communication protocols. Maintain emergency supplies and equipment at backup locations.",
232
+ site_reasoning=f"Alternative work arrangements ensure {process_name} can continue operating when primary {department} facilities are inaccessible.",
233
+
234
+ third_party_vendors_unavailability_strategy=f"Develop relationships with backup vendors for {process_name} critical services, maintain emergency supplier contacts, and establish contingency contracts. Regularly assess vendor reliability and maintain strategic inventory buffers.",
235
+ vendor_reasoning=f"Vendor diversification and contingency planning reduce supply chain risks for {process_name} and ensure continuity of essential external services.",
236
+
237
+ message=f"Fallback strategies generated due to API processing error: {error_msg}" if error_msg else "Fallback strategies generated"
238
+ )
239
 
240
  # Business Continuity Router
241
  business_continuity_router = APIRouter()
 
245
  """
246
  Generate comprehensive business continuity recovery strategies for a business process
247
  """
248
+ request_id = str(uuid.uuid4())
249
+ logger.info(f"Request {request_id}: Starting recovery strategies generation")
250
+ logger.info(f"Request {request_id}: Process - {request.business_process.process_name}")
251
+ logger.info(f"Request {request_id}: Department - {request.business_process.department}")
252
+
253
+ try:
254
+ # Validate input
255
+ if not request.business_process.process_name.strip():
256
+ logger.error(f"Request {request_id}: Empty process name")
257
+ raise HTTPException(status_code=400, detail="Process name cannot be empty")
258
+
259
+ if not request.business_process.department.strip():
260
+ logger.error(f"Request {request_id}: Empty department")
261
+ raise HTTPException(status_code=400, detail="Department cannot be empty")
262
+
263
+ system_prompt = """You are an expert business continuity consultant. Your task is to generate specific, actionable recovery strategies for business processes.
264
+
265
+ CRITICAL INSTRUCTIONS:
266
+ 1. Respond with ONLY a valid JSON object
267
+ 2. Do not include any markdown formatting, code blocks, or additional text
268
+ 3. Each strategy must be 2-3 detailed sentences with specific actionable steps
269
+ 4. Each reasoning must be 1-2 sentences explaining why the strategy fits the specific process
270
+
271
+ Required JSON format (no deviations allowed):
 
 
 
 
 
 
 
272
  {
273
+ "people_unavailability_strategy": "Detailed strategy for handling personnel unavailability with specific steps",
274
+ "people_reasoning": "Clear explanation of why this strategy suits this specific process",
275
+ "technology_data_unavailability_strategy": "Comprehensive strategy for technology and data failures with actionable steps",
276
+ "technology_reasoning": "Explanation of why this technology strategy is appropriate for this process",
277
+ "site_unavailability_strategy": "Detailed strategy for site/facility unavailability with specific actions",
278
+ "site_reasoning": "Why this site strategy is suitable for this specific process",
279
+ "third_party_vendors_unavailability_strategy": "Comprehensive strategy for vendor/supplier disruptions",
280
+ "vendor_reasoning": "Why this vendor strategy is appropriate for this process"
281
  }"""
282
 
283
+ user_message = f"""Generate business continuity recovery strategies for this specific process:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
284
 
285
+ PROCESS DETAILS:
286
+ - Process Name: {request.business_process.process_name}
287
+ - Department: {request.business_process.department}
288
+ - Sub-Department: {request.business_process.sub_department or 'Not specified'}
289
+ - Description: {request.business_process.process_description}
290
+
291
+ ANALYSIS DATA:
292
+ - Impact Analysis: {json.dumps(request.analysis_data.impact_analysis) if request.analysis_data.impact_analysis else 'Not provided'}
293
+ - Minimum Operating Requirements: {json.dumps(request.analysis_data.minimum_operating_requirements) if request.analysis_data.minimum_operating_requirements else 'Not provided'}
294
+
295
+ Generate comprehensive recovery strategies for these four scenarios:
296
+ 1. Key personnel unavailability
297
+ 2. Technology/data system failures
298
+ 3. Primary site/facility unavailability
299
+ 4. Critical third-party vendor/supplier disruptions
300
+
301
+ Focus on actionable, specific strategies tailored to this exact process and department context."""
302
+
303
+ logger.info(f"Request {request_id}: Making API call to GROQ")
304
 
305
+ # Get response from API
306
+ api_response = generate_response(system_prompt, user_message)
 
 
 
 
 
 
 
307
 
308
+ if not api_response:
309
+ logger.error(f"Request {request_id}: Empty API response")
310
+ return generate_fallback_response(request, "Empty API response")
311
 
312
+ logger.info(f"Request {request_id}: API response received, length: {len(api_response)}")
313
+
314
+ # Clean and parse JSON
315
+ try:
316
+ logger.info(f"Request {request_id}: Raw API response length: {len(api_response)}")
317
+ logger.info(f"Request {request_id}: Raw response first 200 chars: {api_response[:200]}")
318
+ logger.info(f"Request {request_id}: Raw response last 200 chars: {api_response[-200:]}")
319
 
320
+ json_str = clean_json_response(api_response)
321
+ strategies_data = json.loads(json_str)
322
+ logger.info(f"Request {request_id}: JSON parsing successful")
 
 
 
 
 
 
 
 
 
 
 
323
 
324
+ except (json.JSONDecodeError, ValueError) as e:
325
+ logger.error(f"Request {request_id}: JSON parsing failed - {str(e)}")
326
+ logger.error(f"Request {request_id}: Raw response preview: {api_response[:300]}...")
327
+ return generate_fallback_response(request, f"JSON parsing failed: {str(e)}")
328
 
329
+ # Validate the parsed data
330
+ try:
331
+ strategies_data = validate_strategies_data(strategies_data)
 
332
 
333
+ response = RecoveryStrategiesResponse(
334
+ success=True,
335
+ people_unavailability_strategy=strategies_data["people_unavailability_strategy"],
336
+ people_reasoning=strategies_data["people_reasoning"],
337
+ technology_data_unavailability_strategy=strategies_data["technology_data_unavailability_strategy"],
338
+ technology_reasoning=strategies_data["technology_reasoning"],
339
+ site_unavailability_strategy=strategies_data["site_unavailability_strategy"],
340
+ site_reasoning=strategies_data["site_reasoning"],
341
+ third_party_vendors_unavailability_strategy=strategies_data["third_party_vendors_unavailability_strategy"],
342
+ vendor_reasoning=strategies_data["vendor_reasoning"],
343
+ message="Successfully generated comprehensive recovery strategies",
344
+ request_id=request_id
345
+ )
346
 
347
+ logger.info(f"Request {request_id}: Successfully generated strategies")
348
+ return response
349
 
350
+ except Exception as e:
351
+ logger.error(f"Request {request_id}: Data validation failed - {str(e)}")
352
+ return generate_fallback_response(request, f"Data validation failed: {str(e)}")
353
 
354
+ except HTTPException:
355
+ # Re-raise HTTP exceptions
356
+ raise
357
 
 
 
358
  except Exception as e:
359
+ logger.error(f"Request {request_id}: Unexpected error - {str(e)}")
360
+ logger.error(f"Request {request_id}: Traceback: {traceback.format_exc()}")
361
+
362
+ # Return fallback response instead of raising HTTP error
363
+ return generate_fallback_response(request, f"Unexpected error: {str(e)}")
364
+
365
+ # Health check endpoint
366
+ @business_continuity_router.get("/api/business-continuity/health")
367
+ def health_check():
368
+ """Health check endpoint"""
369
+ return {
370
+ "status": "healthy",
371
+ "groq_api_configured": bool(GROQ_API_KEY),
372
+ "timestamp": uuid.uuid4().hex
373
+ }