Spaces:
Runtime error
Runtime error
File size: 10,410 Bytes
64cc33d 5c224a0 f7d1072 64cc33d 0c8edc8 64cc33d 8136580 f75809a 0c8edc8 997d665 0c8edc8 f75809a 0c8edc8 5c224a0 0d3b455 f75809a 64cc33d f75809a 64cc33d f75809a 64cc33d 0d3b455 0c8edc8 f75809a 0c8edc8 f75809a 0c8edc8 f7d1072 0c8edc8 f75809a 0c8edc8 f75809a 997d665 0c8edc8 f75809a 0c8edc8 f75809a 0c8edc8 997d665 49c0f36 f75809a 49c0f36 997d665 49c0f36 997d665 7119982 ced5f86 997d665 49c0f36 f75809a fe79a2b 49c0f36 f75809a 49c0f36 f75809a 49c0f36 64cc33d f75809a 64cc33d 0c8edc8 f75809a 64cc33d 0d3b455 f75809a 64cc33d f75809a 64cc33d f75809a fe79a2b 64cc33d fe79a2b 64cc33d fe79a2b 0d3b455 f75809a 0d3b455 997d665 64cc33d f75809a 64cc33d f75809a fe79a2b 0d3b455 997d665 64cc33d 997d665 f75809a ced5f86 f75809a 64cc33d f75809a fe79a2b 0d3b455 f75809a 64cc33d 997d665 64cc33d f75809a 64cc33d 997d665 f75809a 997d665 f75809a 997d665 f75809a 997d665 f75809a 997d665 f75809a 997d665 f75809a 997d665 f75809a 997d665 f75809a 997d665 f75809a 997d665 f75809a 997d665 f75809a 64cc33d ced5f86 64cc33d f75809a 64cc33d 49c0f36 f75809a 7119982 f75809a fe79a2b f75809a 7119982 997d665 f75809a 997d665 f75809a 997d665 f75809a 997d665 f75809a 49c0f36 f75809a 0d3b455 997d665 f75809a 997d665 f75809a 997d665 1667a68 f75809a 64cc33d f75809a 64cc33d f75809a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 | import requests
import json
import logging
import os
from datetime import datetime
from simple_salesforce import Salesforce, SalesforceAuthenticationFailed
from flask import Flask, jsonify, request, render_template, redirect, url_for
# Configure logging
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[logging.StreamHandler()]
)
logger = logging.getLogger(__name__)
# Load environment variables (set these in your environment)
SF_USERNAME = os.getenv("SF_USERNAME") # e.g., Ai@Coach.com
SF_PASSWORD = os.getenv("SF_PASSWORD") # e.g., Teja90325@
SF_SECURITY_TOKEN = os.getenv("SF_SECURITY_TOKEN") # e.g., clceSdBgQ30Rx9BSC66gAcRx
SF_DOMAIN = os.getenv("SF_DOMAIN", "login") # default to "login"
HUGGINGFACE_API_KEY = os.getenv("HUGGINGFACE_API_KEY") # your Hugging Face API token
# Validate environment variables
if not all([SF_USERNAME, SF_PASSWORD, SF_SECURITY_TOKEN, HUGGINGFACE_API_KEY]):
logger.error("One or more environment variables are missing.")
raise ValueError("Please set SF_USERNAME, SF_PASSWORD, SF_SECURITY_TOKEN, and HUGGINGFACE_API_KEY environment variables.")
# Constants
HUGGING_FACE_API_URL = "https://api-inference.huggingface.co/models/distilgpt2"
HUGGING_FACE_API_TOKEN = HUGGINGFACE_API_KEY
# Initialize Flask app
app = Flask(__name__)
def test_salesforce_connection():
"""
Test the Salesforce connection.
"""
try:
logger.debug("Connecting to Salesforce with username: %s", SF_USERNAME)
sf = Salesforce(
username=SF_USERNAME,
password=SF_PASSWORD,
security_token=SF_SECURITY_TOKEN,
domain=SF_DOMAIN
)
# Verify connection
sf.query("SELECT Id FROM Account LIMIT 1")
logger.info("Connected to Salesforce successfully.")
return True, None, sf
except SalesforceAuthenticationFailed as e:
logger.error("Salesforce authentication failed: %s", e)
return False, f"Authentication failed: {str(e)}", None
except Exception as e:
logger.error("Salesforce connection error: %s", e)
return False, f"Connection error: {str(e)}", None
def fetch_project_data(project_id):
"""
Fetch Project__c record data.
"""
success, error, sf = test_salesforce_connection()
if not success:
return None, error
try:
query = f"""
SELECT Supervisor_ID__c, Role__c, Project_ID__c, Weather__c, Milestones__c, Reflection_Log__c
FROM Project__c
WHERE Id = '{project_id}'
LIMIT 1
"""
result = sf.query(query)
if result['totalSize'] > 0:
record = result['records'][0]
return {
'supervisor_id': record.get('Supervisor_ID__c', ''),
'role': record.get('Role__c', ''),
'project_id': record.get('Project_ID__c', ''),
'weather': record.get('Weather__c', ''),
'milestones': record.get('Milestones__c', ''),
'reflection': record.get('Reflection_Log__c', '')
}, None
else:
return None, "No Project__c record found."
except Exception as e:
logger.error("Error fetching project data: %s", e)
return None, f"Error fetching project data: {str(e)}"
def generate_coaching_output(data):
"""
Generate coaching output using Hugging Face API.
"""
logger.info("Generating coaching output for supervisor %s", data['supervisor_id'])
milestones_json = json.dumps(data['milestones'], indent=2)
prompt = f"""
You are an AI Coach for construction site supervisors. Based on the following data, generate a daily checklist, three focus tips, and a motivational quote. Ensure outputs are concise, actionable, and tailored to the supervisor's role, project status, and reflection log.
Supervisor Role: {data['role']}
Project Milestones: {milestones_json}
Reflection Log: {data['reflection_log']}
Weather: {data['weather']}
Format the response as JSON:
{{
"checklist": ["item1", "item2", ...],
"tips": ["tip1", "tip2", "tip3"],
"quote": "motivational quote"
}}
"""
headers = {
"Authorization": f"Bearer {HUGGING_FACE_API_TOKEN}",
"Content-Type": "application/json"
}
payload = {
"inputs": prompt,
"parameters": {
"max_length": 200,
"temperature": 0.7,
"top_p": 0.9,
"debug": True
}
}
try:
response = requests.post(HUGGING_FACE_API_URL, headers=headers, json=payload, timeout=10)
response.raise_for_status()
result = response.json()
generated_text = result[0]["generated_text"] if isinstance(result, list) else result["generated_text"]
start_idx = generated_text.find('{')
end_idx = generated_text.rfind('}') + 1
if start_idx == -1 or end_idx == 0:
raise ValueError("No JSON found in LLM output")
json_str = generated_text[start_idx:end_idx]
output = json.loads(json_str)
logger.info("Generated coaching output successfully.")
return output, None
except requests.exceptions.HTTPError as e:
logger.error("Hugging Face API HTTP error: %s", e)
return None, f"Hugging Face API HTTP error: {str(e)}"
except (json.JSONDecodeError, ValueError) as e:
logger.error("Error parsing LLM output: %s", e)
return None, f"Error parsing LLM output: {str(e)}"
except Exception as e:
logger.error("Unexpected error during API call: %s", e)
return None, f"API call error: {str(e)}"
def save_to_coaching_log(output, supervisor_id, project_id):
"""
Save coaching output to Salesforce Coaching_Log__c.
"""
if not output:
logger.error("No coaching output to save.")
return False, "No coaching output to save."
success, error, sf = test_salesforce_connection()
if not success:
return False, error
try:
record = {
"Supervisor_ID__c": supervisor_id,
"Project_ID__c": project_id,
"Daily_Checklist__c": "\n".join(output.get("checklist", [])),
"Suggested_Tips__c": "\n".join(output.get("tips", [])),
"Quote__c": output.get("quote", ""),
"Generated_Date__c": datetime.now().strftime("%Y-%m-%d")
}
sf.Coaching_Log__c.create(record)
logger.info("Coaching log saved successfully.")
return True, None
except Exception as e:
logger.error("Error saving coaching log: %s", e)
return False, f"Error saving coaching log: {str(e)}"
@app.route('/process_new_project', methods=['POST'])
def process_new_project():
"""
Endpoint to process new Project__c creation.
"""
data = request.get_json()
if not data or 'projectId' not in data:
logger.error("Invalid request: missing projectId.")
return jsonify({"status": "error", "message": "projectId not provided"}), 400
project_id = data['projectId']
logger.info("Processing project ID: %s", project_id)
# Fetch project data from Salesforce
project_data, fetch_error = fetch_project_data(project_id)
if fetch_error:
logger.error("Error fetching project data: %s", fetch_error)
return jsonify({"status": "error", "message": fetch_error}), 500
if not project_data:
logger.error("No data found for project ID: %s", project_id)
return jsonify({"status": "error", "message": "No project data found"}), 404
# Prepare data for AI
data_for_ai = {
'supervisor_id': project_data['supervisor_id'],
'role': project_data['role'],
'project_id': project_data['project_id'],
'milestones': [m.strip() for m in project_data['milestones'].split(',') if m.strip()],
'reflection_log': project_data['reflection'],
'weather': project_data['weather']
}
# Generate coaching output
coaching_output, gen_error = generate_coaching_output(data_for_ai)
if gen_error:
logger.error("Error generating coaching output: %s", gen_error)
return jsonify({"status": "error", "message": gen_error}), 500
# Save to Salesforce
success, save_error = save_to_coaching_log(coaching_output, project_data['supervisor_id'], project_data['project_id'])
if not success:
logger.error("Error saving coaching log: %s", save_error)
return jsonify({"status": "error", "message": save_error}), 500
return jsonify({"status": "success", "message": "Coaching log generated and saved."}), 200
@app.route('/ui', methods=['GET'])
def ui():
"""
Serve UI with latest coaching log.
"""
form_data = {}
output = {}
error_msg = ""
success, error_msg, sf = test_salesforce_connection()
if not success:
return render_template('index.html', form_data={}, output={}, error=error_msg)
try:
query = """
SELECT Supervisor_ID__c, Project_ID__c, Daily_Checklist__c, Suggested_Tips__c, Quote__c
FROM Coaching_Log__c
ORDER BY Generated_Date__c DESC
LIMIT 1
"""
result = sf.query(query)
if result['totalSize'] > 0:
record = result['records'][0]
form_data = {
'supervisor_id': record.get('Supervisor_ID__c', ''),
'project_id': record.get('Project_ID__c', '')
}
output = {
'checklist': record.get('Daily_Checklist__c', '').split('\n'),
'tips': record.get('Suggested_Tips__c', '').split('\n'),
'quote': record.get('Quote__c', '')
}
else:
error_msg = "No coaching logs found."
except Exception as e:
logger.error("Error fetching coaching logs: %s", e)
error_msg = f"Error fetching coaching logs: {str(e)}"
return render_template('index.html', form_data=form_data, output=output, error=error_msg)
@app.route('/', methods=['GET'])
def root():
"""
Redirect root to /ui.
"""
return redirect(url_for('ui'))
@app.route('/health', methods=['GET'])
def health():
"""
Basic health check.
"""
return jsonify({"status": "healthy", "message": "Application is running"}), 200
if __name__ == "__main__":
# Run Flask app
app.run(host='0.0.0.0', port=7860) |