|
import requests |
|
import json |
|
import logging |
|
from datetime import datetime |
|
from simple_salesforce import Salesforce |
|
from flask import Flask, jsonify, request, render_template, redirect, url_for |
|
|
|
|
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') |
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
HUGGING_FACE_API_URL = "https://api-inference.huggingface.co/models/distilgpt2" |
|
HUGGING_FACE_API_TOKEN = "your_hugging_face_api_token_here" |
|
|
|
|
|
SALESFORCE_USERNAME = "[email protected]" |
|
SALESFORCE_PASSWORD = "Teja90325@" |
|
SALESFORCE_SECURITY_TOKEN = "clceSdBgQ30Rx9BSC66gAcRx" |
|
SALESFORCE_DOMAIN = "login.salesforce.com" |
|
|
|
|
|
if not HUGGING_FACE_API_TOKEN: |
|
logger.error("HUGGING_FACE_API_TOKEN is not set") |
|
raise ValueError("HUGGING_FACE_API_TOKEN must be provided") |
|
if not HUGGING_FACE_API_URL.startswith("https://api-inference.huggingface.co/models/"): |
|
logger.error("Invalid HUGGING_FACE_API_URL: %s", HUGGING_FACE_API_URL) |
|
raise ValueError("HUGGING_FACE_API_URL must point to a valid Hugging Face model") |
|
if not all([SALESFORCE_USERNAME, SALESFORCE_PASSWORD, SALESFORCE_SECURITY_TOKEN]): |
|
logger.error("Salesforce credentials are incomplete") |
|
raise ValueError("Salesforce credentials must be set") |
|
|
|
|
|
app = Flask(__name__) |
|
|
|
def generate_coaching_output(data): |
|
""" |
|
Generate daily checklist and tips using Hugging Face LLM. |
|
""" |
|
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 |
|
} |
|
} |
|
|
|
try: |
|
response = requests.post(HUGGING_FACE_API_URL, headers=headers, json=payload, timeout=5) |
|
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: |
|
logger.error("No valid JSON found in LLM output") |
|
raise ValueError("No valid JSON found in LLM output") |
|
|
|
json_str = generated_text[start_idx:end_idx] |
|
output = json.loads(json_str) |
|
logger.info("Successfully generated coaching output") |
|
return output |
|
|
|
except requests.exceptions.HTTPError as e: |
|
logger.error("Hugging Face API HTTP error: %s", e) |
|
return None |
|
except (json.JSONDecodeError, ValueError) as e: |
|
logger.error("Error parsing LLM output: %s", e) |
|
return None |
|
except Exception as e: |
|
logger.error("Unexpected error in Hugging Face API call: %s", e) |
|
return None |
|
|
|
def save_to_salesforce(output, supervisor_id, project_id): |
|
""" |
|
Save coaching output to Salesforce Supervisor_AI_Coaching__c object. |
|
""" |
|
if not output: |
|
logger.error("No coaching output to save") |
|
return False |
|
|
|
try: |
|
sf = Salesforce( |
|
username=SALESFORCE_USERNAME, |
|
password=SALESFORCE_PASSWORD, |
|
security_token=SALESFORCE_SECURITY_TOKEN, |
|
domain=SALESFORCE_DOMAIN |
|
) |
|
logger.info("Connected to Salesforce") |
|
|
|
coaching_record = { |
|
"Supervisor_ID__c": supervisor_id, |
|
"Project_ID__c": project_id, |
|
"Daily_Checklist__c": "\n".join(output["checklist"]), |
|
"Suggested_Tips__c": "\n".join(output["tips"]), |
|
"Quote__c": output["quote"], |
|
"Generated_Date__c": datetime.now().strftime("%Y-%m-%d") |
|
} |
|
|
|
sf.Supervisor_AI_Coaching__c.upsert( |
|
f"Supervisor_ID__c/{supervisor_id}_{datetime.now().strftime('%Y-%m-%d')}", |
|
coaching_record |
|
) |
|
logger.info("Successfully saved coaching record to Salesforce for supervisor %s", supervisor_id) |
|
return True |
|
|
|
except Exception as e: |
|
logger.error("Salesforce error: %s", e) |
|
return False |
|
|
|
def fetch_salesforce_data(supervisor_id, project_id): |
|
""" |
|
Fetch coaching data from Salesforce for a given supervisor and project. |
|
""" |
|
try: |
|
sf = Salesforce( |
|
username=SALESFORCE_USERNAME, |
|
password=SALESFORCE_PASSWORD, |
|
security_token=SALESFORCE_SECURITY_TOKEN, |
|
domain=SALESFORCE_DOMAIN |
|
) |
|
logger.info("Connected to Salesforce for data fetch") |
|
|
|
query = f""" |
|
SELECT Daily_Checklist__c, Suggested_Tips__c, Quote__c, Generated_Date__c |
|
FROM Supervisor_AI_Coaching__c |
|
WHERE Supervisor_ID__c = '{supervisor_id}' AND Project_ID__c = '{project_id}' |
|
ORDER BY Generated_Date__c DESC |
|
LIMIT 1 |
|
""" |
|
result = sf.query(query) |
|
if result['totalSize'] > 0: |
|
record = result['records'][0] |
|
return { |
|
'checklist': record['Daily_Checklist__c'].split('\n') if record['Daily_Checklist__c'] else [], |
|
'tips': record['Suggested_Tips__c'].split('\n') if record['Suggested_Tips__c'] else [], |
|
'quote': record['Quote__c'] or '' |
|
} |
|
else: |
|
logger.info("No coaching data found for supervisor %s and project %s", supervisor_id, project_id) |
|
return None |
|
|
|
except Exception as e: |
|
logger.error("Salesforce fetch error: %s", e) |
|
return None |
|
|
|
@app.route('/', methods=['GET']) |
|
def redirect_to_ui(): |
|
""" |
|
Redirect root URL to the UI. |
|
""" |
|
return redirect(url_for('ui')) |
|
|
|
@app.route('/ui', methods=['GET', 'POST']) |
|
def ui(): |
|
""" |
|
Serve the HTML user interface and handle form submissions. |
|
""" |
|
form_data = {} |
|
output = {} |
|
error = "" |
|
|
|
if request.method == 'POST': |
|
action = request.form.get('action') |
|
form_data = { |
|
'supervisor_id': request.form.get('supervisor_id', ''), |
|
'role': request.form.get('role', ''), |
|
'project_id': request.form.get('project_id', ''), |
|
'weather': request.form.get('weather', ''), |
|
'milestones': request.form.get('milestones', ''), |
|
'reflection': request.form.get('reflection', '') |
|
} |
|
|
|
if action == 'generate': |
|
|
|
if not all([form_data['supervisor_id'], form_data['role'], form_data['project_id'], |
|
form_data['weather'], form_data['milestones'], form_data['reflection']]): |
|
error = "Error: All fields are required." |
|
else: |
|
|
|
sf_data = fetch_salesforce_data(form_data['supervisor_id'], form_data['project_id']) |
|
if sf_data: |
|
output = sf_data |
|
else: |
|
|
|
data = { |
|
'supervisor_id': form_data['supervisor_id'], |
|
'role': form_data['role'], |
|
'project_id': form_data['project_id'], |
|
'milestones': [m.strip() for m in form_data['milestones'].split(',') if m.strip()], |
|
'reflection_log': form_data['reflection'], |
|
'weather': form_data['weather'] |
|
} |
|
coaching_output = generate_coaching_output(data) |
|
if coaching_output: |
|
success = save_to_salesforce(coaching_output, data['supervisor_id'], data['project_id']) |
|
if success: |
|
output = coaching_output |
|
else: |
|
error = "Error: Failed to save to Salesforce." |
|
else: |
|
error = "Error: Failed to generate coaching output." |
|
|
|
return render_template('index.html', form_data=form_data, output=output, error=error) |
|
|
|
@app.route('/health', methods=['GET']) |
|
def health_check(): |
|
""" |
|
Health check endpoint. |
|
""" |
|
return jsonify({"status": "healthy", "message": "Application is running"}), 200 |
|
|
|
if __name__ == "__main__": |
|
app.run(host="0.0.0.0", port=7860) |