Spaces:
Build error
Build error
import requests | |
import json | |
import os | |
import logging | |
from datetime import datetime | |
from dotenv import load_dotenv | |
from simple_salesforce import Salesforce | |
from flask import Flask, jsonify, request, render_template, redirect, url_for | |
# Configure logging | |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') | |
logger = logging.getLogger(__name__) | |
# Load environment variables | |
load_dotenv() | |
# Hugging Face API configuration | |
HUGGING_FACE_API_URL = os.getenv("HUGGING_FACE_API_URL", "https://api-inference.huggingface.co/models/distilgpt2") | |
HUGGING_FACE_API_TOKEN = os.getenv("HUGGING_FACE_API_TOKEN") | |
# Salesforce configuration | |
SALESFORCE_USERNAME = os.getenv("SALESFORCE_USERNAME") | |
SALESFORCE_PASSWORD = os.getenv("SALESFORCE_PASSWORD") | |
SALESFORCE_SECURITY_TOKEN = os.getenv("SALESFORCE_SECURITY_TOKEN") | |
SALESFORCE_DOMAIN = os.getenv("SALESFORCE_DOMAIN", "login") | |
# Validate environment variables | |
if not HUGGING_FACE_API_TOKEN: | |
logger.error("HUGGING_FACE_API_TOKEN is not set") | |
raise ValueError("HUGGING_FACE_API_TOKEN environment variable is not set") | |
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") | |
# Initialize Flask app | |
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 redirect_to_ui(): | |
""" | |
Redirect root URL to the UI. | |
""" | |
return redirect(url_for('ui')) | |
def ui(): | |
""" | |
Serve the HTML user interface. | |
""" | |
return render_template('index.html') | |
def generate_endpoint(): | |
""" | |
Endpoint to generate coaching output based on supervisor data. | |
""" | |
try: | |
data = request.get_json() | |
if not data or not all(key in data for key in ['supervisor_id', 'role', 'project_id', 'milestones', 'reflection_log', 'weather']): | |
return jsonify({"status": "error", "message": "Invalid or missing supervisor data"}), 400 | |
coaching_output = generate_coaching_output(data) | |
if coaching_output: | |
success = save_to_salesforce(coaching_output, data["supervisor_id"], data["project_id"]) | |
if success: | |
return jsonify({"status": "success", "output": coaching_output}), 200 | |
else: | |
return jsonify({"status": "error", "message": "Failed to save to Salesforce"}), 500 | |
else: | |
return jsonify({"status": "error", "message": "Failed to generate coaching output"}), 500 | |
except Exception as e: | |
logger.error("Error in generate endpoint: %s", e) | |
return jsonify({"status": "error", "message": str(e)}), 500 | |
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=int(os.getenv("PORT", 7860))) |