| 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 |
|
|
| |
| logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') |
| logger = logging.getLogger(__name__) |
|
|
| |
| load_dotenv() |
|
|
| |
| 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_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") |
|
|
| |
| 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") |
|
|
| |
| 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 |
|
|
| @app.route('/', methods=['GET']) |
| def redirect_to_ui(): |
| """ |
| Redirect root URL to the UI. |
| """ |
| return redirect(url_for('ui')) |
|
|
| @app.route('/ui', methods=['GET']) |
| def ui(): |
| """ |
| Serve the HTML user interface. |
| """ |
| return render_template('index.html') |
|
|
| @app.route('/generate', methods=['POST']) |
| 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 |
|
|
| @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=int(os.getenv("PORT", 7860))) |