Texbase / AgenticControl /draft_reply.py
Asad999's picture
Initial clean deployment for Hugging Face Spaces (v5 - final fix)
d712cef
Raw
History Blame Contribute Delete
5.8 kB
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}"}))