File size: 13,029 Bytes
d712cef
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
# 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]<br><br>Hi Team,<br><br>..."
          }}
        }}
        """

        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)