# Button is pressed or 24 hours have passed since the email was sent. # Then send a follow-up email. # An email copy will be made in the followup database # and unique identifier to maintain tracing in the # new database once the user has approved it will be sen # and will again come in the followup database with same unique identifier # and maximum allowed feed backs are 3 on which further work will be done # # #------------------------------------------------------------------------------------------- """ followupTracker(id): //you will get the email from Database/FollowUps/sent_emails.db using 'id' make a separate database for such cases in the Database/EmailsUnderReview and also add a column for context which will come along from the database " company_email TEXT, role TEXT, date_applied DATETIME, followup_date DATETIME, status TEXT, Unique_application_id TEXT, message_id TEXT, body_json TEXT )" in the new database there will be a new column for overall summary and write it in bullet points as there might be existing bullet points so simple add an extra so basically context contains all the summaries of past emails which will. be used to generate a followup email and rest everything same as body json will contain new email generated as a followup and will be saved in the database so followups are handled properly and no problem occurs the column in new database:"overall_summary" and remember that all other informations should be carried along especially the unique_application_id Use the github token to generate new email "def generate_application_body(company_data: dict, user_data: dict) -> str: if not GITHUB_TOKEN: print("Error: GITHUB_TOKEN not found in environment variables.") return "{}" # Initialize the Azure/GitHub inference client try: endpoint = "https://models.github.ai/inference" client = ChatCompletionsClient( endpoint=endpoint, credential=AzureKeyCredential(GITHUB_TOKEN), ) print("Client initialized successfully") except Exception as e: print(f"Error initializing client: {e}") return "{}"" use "def call_llm(system_prompt, user_query): payload = { "system_prompt": system_prompt, "query": user_query, "max_new_tokens": 1000 } response = requests.post(LLM_URL, json=payload) if response.status_code == 200: return response.json()["response"] raise Exception(f"LLM Error: {response.text}")" to summarize the emails """ import os import sqlite3 import json import requests from datetime import datetime # Import the Azure/GitHub inference client from azure.ai.inference import ChatCompletionsClient from azure.ai.inference.models import SystemMessage, UserMessage from azure.core.credentials import AzureKeyCredential from dotenv import load_dotenv # ========================================== # CONFIGURATION & LLM HELPERS # ========================================== # It is best practice to set this in your terminal (export GITHUB_TOKEN="..."), # but I have included your fallback token here for easy testing. load_dotenv(dotenv_path=os.path.join(os.environ.get('WORKSPACE_ROOT', '.'), 'backend/.env')) GITHUB_TOKEN = os.getenv("GITHUB_TOKEN") LLM_URL = "https://unscotched-devon-interpapillary.ngrok-free.dev/generate" def call_llm(system_prompt: str, user_query: str) -> str: """ Calls your custom local LLM endpoint to summarize the previous email. """ payload = { "system_prompt": system_prompt, "query": user_query, "max_new_tokens": 1000 } try: response = requests.post(LLM_URL, json=payload) response.raise_for_status() # Raises an error for bad HTTP status codes return response.json().get("response", "No response generated.") except Exception as e: raise Exception(f"Local LLM Error: {e}") def generate_application_body(company_email: str, company_name: str, context: str) -> str: """ Uses GitHub Models (gpt-4o) to generate the new follow-up email. """ if not GITHUB_TOKEN: print("❌ Error: GITHUB_TOKEN not found.") return "{}" try: endpoint = "https://models.github.ai/inference" client = ChatCompletionsClient( endpoint=endpoint, credential=AzureKeyCredential(GITHUB_TOKEN), ) print("🤖 GitHub Client initialized successfully.") system_prompt = "You are an AI assistant helping a textile sales manager write professional follow-up emails for B2B outreach." user_prompt = f""" Company: {company_name} Company Email: {company_email} Past Context / Summaries: {context} Write a polite, concise follow-up email asking if they've had a chance to review our previous proposal. Format the output STRICTLY as valid JSON. Do not include markdown formatting like ```json. Structure: {{ "body": {{ "generated_content": "Subject: Following up on our partnership proposal - [Company Name]

Hi Team,

..." }} }} """ response = client.complete( messages=[ SystemMessage(content=system_prompt), UserMessage(content=user_prompt) ], model="gpt-4o", temperature=0.7, max_tokens=1000 ) # Clean up any markdown blocks if the LLM adds them content = response.choices[0].message.content.strip() if content.startswith("```json"): content = content[7:-3].strip() elif content.startswith("```"): content = content[3:-3].strip() return content except Exception as e: print(f"❌ Error generating follow-up email: {e}") return "{}" # ========================================== # MAIN TRACKER LOGIC # ========================================== def followupTracker(record_id): """ Extracts the old email, summarizes it, generates a new follow-up email, and saves the entire package to the EmailsUnderReview database. """ source_db_path = os.path.join(os.environ.get('WORKSPACE_ROOT', '.'), 'Database/FollowUps/sent_emails.db') dest_dir = os.path.join(os.environ.get('WORKSPACE_ROOT', '.'), 'Database/EmailsUnderReview') os.makedirs(dest_dir, exist_ok=True) dest_db_path = os.path.join(dest_dir, 'followups_under_review.db') # 1. FETCH FROM SOURCE DATABASE try: source_conn = sqlite3.connect(source_db_path) source_conn.row_factory = sqlite3.Row cursor = source_conn.cursor() # Works with either the integer ID or the 20-digit string ID cursor.execute("SELECT * FROM sent_applications WHERE id = ? OR Unique_application_id = ?", (record_id, str(record_id))) record = cursor.fetchone() if not record: print(f"❌ No record found in sent_emails.db with ID: {record_id}") return company_email = record["company_email"] company_name = record["company_name"] generated_subject = record["generated_subject"] followup_date = record["followup_date"] unique_application_id = record["Unique_application_id"] message_id = record["message_id"] old_body_json_str = record["body_json"] except sqlite3.Error as e: print(f"❌ Source Database error: {e}") return finally: if 'source_conn' in locals() and source_conn: source_conn.close() # 2. EXTRACT OLD EMAIL TEXT & SUMMARIZE IT try: old_data = json.loads(old_body_json_str) # Dig into the JSON to get just the actual email text old_email_text = old_data.get("body", {}).get("generated_content", "No content found.") except Exception: # Fallback if the database string isn't perfectly formatted JSON old_email_text = old_body_json_str try: print("📝 Summarizing previous email via Local LLM...") summary_sys_prompt = "You summarize emails concisely into exactly one short sentence." summary_query = f"Summarize this email:\n{old_email_text}" new_summary_text = call_llm(summary_sys_prompt, summary_query) current_date = datetime.now().strftime('%Y-%m-%d') new_bullet = f"• {current_date}: {new_summary_text.strip()}" print(f"✅ Summary generated: {new_bullet}") except Exception as e: print(f"⚠️ Summarization skipped or failed: {e}") new_bullet = f"• {datetime.now().strftime('%Y-%m-%d')}: Follow-up initiated for {company_name}." # 3. SAVE/UPDATE DESTINATION DATABASE try: dest_conn = sqlite3.connect(dest_db_path) dest_cursor = dest_conn.cursor() dest_cursor.execute(''' CREATE TABLE IF NOT EXISTS followups_pending ( id INTEGER PRIMARY KEY AUTOINCREMENT, company_email TEXT, company_name TEXT, generated_subject TEXT, followup_date DATETIME, status TEXT, Unique_application_id TEXT, message_id TEXT, body_json TEXT, context TEXT, overall_summary TEXT ) ''') # Check if this application thread already exists in the UnderReview DB dest_cursor.execute("SELECT overall_summary, context FROM followups_pending WHERE Unique_application_id = ?", (unique_application_id,)) existing_record = dest_cursor.fetchone() if existing_record: existing_summary = existing_record[0] if existing_record[0] else "" overall_summary = f"{existing_summary}\n{new_bullet}" context = existing_record[1] if existing_record[1] else f"Company: {company_name} ({company_email})" is_update = True else: overall_summary = new_bullet context = f"Company: {company_name} ({company_email})\nInitial Outreach: {generated_subject}" is_update = False # Build the complete context string to feed to the GitHub Model full_context_for_llm = f"{context}\n\nEmail History:\n{overall_summary}" # 4. GENERATE THE NEW FOLLOW-UP EMAIL JSON print("⚙️ Generating new follow-up email draft via GitHub Models...") new_email_json_str = generate_application_body(company_email, company_name, full_context_for_llm) status = "Draft Generated - Pending Review" # 5. COMMIT TO DESTINATION DB if is_update: dest_cursor.execute(''' UPDATE followups_pending SET status = ?, body_json = ?, overall_summary = ?, context = ? WHERE Unique_application_id = ? ''', (status, new_email_json_str, overall_summary, full_context_for_llm, unique_application_id)) print(f"✅ Updated existing tracker and saved new draft. (ID: {unique_application_id})") else: dest_cursor.execute(''' INSERT INTO followups_pending (company_email, company_name, generated_subject, followup_date, status, Unique_application_id, message_id, body_json, context, overall_summary) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''', ( company_email, company_name, generated_subject, followup_date, status, unique_application_id, message_id, new_email_json_str, full_context_for_llm, overall_summary )) print(f"✅ Created new tracker and saved first follow-up draft. (ID: {unique_application_id})") dest_conn.commit() except sqlite3.Error as e: print(f"❌ Destination Database error: {e}") finally: if 'dest_conn' in locals() and dest_conn: dest_conn.close() print("Done! 🎉") # ========================================== # CLI ENTRY POINT # ========================================== if __name__ == "__main__": import sys try: raw_input = sys.stdin.read().strip() if not raw_input: print(json.dumps({"ok": False, "error": "No input provided"})) sys.exit(1) payload = json.loads(raw_input) record_id = payload.get("id") if not record_id: print(json.dumps({"ok": False, "error": "Missing 'id' in input"})) sys.exit(1) # Run the tracker logic followupTracker(record_id) # Output success for the server to parse print(json.dumps({"ok": True})) except Exception as e: print(json.dumps({"ok": False, "error": str(e)})) sys.exit(1)