File size: 5,801 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 | import sys
import json
import sqlite3
import os
import random
import string
from datetime import datetime
from dotenv import load_dotenv
from azure.ai.inference import ChatCompletionsClient
from azure.core.credentials import AzureKeyCredential
ROOT_DIR = os.environ.get('WORKSPACE_ROOT', '.')
load_dotenv(dotenv_path=os.path.join(ROOT_DIR, 'backend/.env'))
INBOX_DB = os.path.join(ROOT_DIR, 'Database/Inbox/inbox.db')
REVIEW_DB = os.path.join(ROOT_DIR, 'Database/EmailsUnderReview/emailsUnderReview.db')
SENDER = {
"company": "Arooj Enterprises",
"name": "Asad Irfan",
"title": "Senior Marketing Manager",
"website": "www.texbase.com",
"certs": "ISO 14001, SEDEX, and OEKO-TEX",
"capacity": "150,000 units/month"
}
def init_review_db():
conn = sqlite3.connect(REVIEW_DB)
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS tracking (
id INTEGER PRIMARY KEY AUTOINCREMENT,
company_name TEXT,
website TEXT,
address TEXT,
total_shipments TEXT,
top_suppliers TEXT,
hs_codes TEXT,
company_description TEXT,
key_executives TEXT,
generated_subject TEXT,
status TEXT DEFAULT 'pending',
body_json TEXT,
company_email TEXT,
deep_research_pdf TEXT
)
''')
conn.commit()
return conn
def draft_reply(inbox_id):
if not GITHUB_TOKEN:
print(json.dumps({"error": "GITHUB_TOKEN not found."}))
return
# 1. Fetch thread history
conn_inbox = sqlite3.connect(INBOX_DB)
c_inbox = conn_inbox.cursor()
c_inbox.execute("SELECT thread_id, company_email, company_name, subject, last_messages_json FROM inbox_threads WHERE id = ?", (inbox_id,))
row = c_inbox.fetchone()
if not row:
print(json.dumps({"error": f"Inbox thread {inbox_id} not found."}))
return
thread_id, company_email, company_name, subject, last_messages_json = row
history = json.loads(last_messages_json)
history_text = "\n\n".join([f"From: {msg['from']}\nDate: {msg['date']}\nMessage:\n{msg['snippet']}" for msg in history])
# 2. Call LLM
client = ChatCompletionsClient(
endpoint="https://models.github.ai/inference",
credential=AzureKeyCredential(GITHUB_TOKEN),
)
prompt = f"""You are an expert B2B sales email writer for {SENDER['company']}.
You are replying to an email thread with {company_name} ({company_email}).
βββ EMAIL THREAD HISTORY βββ
{history_text}
βββ INSTRUCTIONS βββ
- Write a professional, concise, and persuasive reply to the most recent message in the thread.
- Address their questions or concerns directly.
- Maintain our company persona: we are a garment manufacturer from Pakistan with a capacity of {SENDER['capacity']} and certifications {SENDER['certs']}.
- Keep it under 150 words.
- Structure it cleanly with paragraphs separated by \\n\\n.
CRITICAL OUTPUT REQUIREMENT:
- Output strictly valid JSON with exactly two keys: "subject" and "body".
- "subject": Keep the thread subject but ensure it starts with 'Re: ' if not already. Current subject: {subject}
- "body": Full email including greeting and sign-off as: {SENDER['name']} | {SENDER['title']} | {SENDER['company']} | {SENDER['website']}
- THE BODY MUST INCLUDE \\n\\n (escaped newlines) BETWEEN EVERY PARAGRAPH. Do NOT output a single flat block of text!
"""
try:
response = client.complete(
messages=[
{"role": "system", "content": "You are a professional B2B cold email response writer. Output strictly in JSON format without markdown code blocks."},
{"role": "user", "content": prompt}
],
model="gpt-4o",
temperature=0.7,
max_tokens=1000
)
reply_json_str = response.choices[0].message.content.strip()
if reply_json_str.startswith("```json"):
reply_json_str = reply_json_str[7:]
if reply_json_str.endswith("```"):
reply_json_str = reply_json_str[:-3]
reply_data = json.loads(reply_json_str)
new_subject = reply_data.get("subject", f"Re: {subject}")
new_body = reply_data.get("body", "")
# 3. Store in Review DB
conn_review = init_review_db()
c_review = conn_review.cursor()
# Build payload similar to EmailGenerator
body_json_payload = {
"body": {
"generated_content": new_body,
"subject": new_subject,
"outreach_data": {
"company_name": company_name,
"company_email": company_email,
"inbox_thread_id": thread_id # Store thread ID so we know it's a reply and can send it in the same thread later
}
}
}
c_review.execute('''
INSERT INTO tracking (company_name, company_email, generated_subject, status, body_json)
VALUES (?, ?, ?, 'pending', ?)
''', (company_name, company_email, new_subject, json.dumps(body_json_payload)))
# 4. Update Inbox thread status
c_inbox.execute("UPDATE inbox_threads SET status = 'drafted' WHERE id = ?", (inbox_id,))
conn_inbox.commit()
conn_review.commit()
print(json.dumps({"success": True}))
except Exception as e:
print(json.dumps({"error": str(e)}))
if __name__ == "__main__":
try:
payload = json.loads(sys.stdin.read())
inbox_id = int(payload.get('inbox_id'))
draft_reply(inbox_id)
except Exception as e:
print(json.dumps({"error": f"Invalid input: {e}"}))
|