Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -576,7 +576,7 @@ async def generate_campaign_sequences(campaign_id: int, request: GenerateSequenc
|
|
576 |
return {
|
577 |
"ok": True,
|
578 |
"message": "Sequences generated and saved successfully",
|
579 |
-
"generated_sequences": [seq
|
580 |
"save_result": result
|
581 |
}
|
582 |
|
@@ -836,6 +836,16 @@ def generate_template_welcome_closing_messages(lead_data: Dict[str, Any]) -> Dic
|
|
836 |
}
|
837 |
|
838 |
async def generate_sequences_with_llm(job_description: str) -> List[CampaignSequence]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
839 |
"""Generate email sequences using LangChain and OpenAI based on job description"""
|
840 |
|
841 |
if not LANGCHAIN_AVAILABLE:
|
@@ -848,46 +858,34 @@ async def generate_sequences_with_llm(job_description: str) -> List[CampaignSequ
|
|
848 |
return await generate_template_sequences(job_description)
|
849 |
|
850 |
llm = ChatOpenAI(
|
851 |
-
model="gpt-
|
852 |
temperature=0.7,
|
853 |
openai_api_key=openai_api_key
|
854 |
)
|
|
|
855 |
|
856 |
-
system_prompt = """You are an expert email sequence generator for recruitment campaigns.
|
857 |
-
|
858 |
Generate ONLY the subject lines and email body content for 3 professional email sequences.
|
859 |
-
|
|
|
|
|
860 |
Email Sequence Structure:
|
861 |
-
1. INTRODUCTION (Day 1): Ask for consent and interest in the role
|
862 |
-
2. OUTREACH (Day 3): Provide detailed job information
|
863 |
3. FOLLOW-UP (Day 5): Follow up on updates and next steps
|
864 |
-
|
865 |
Requirements:
|
866 |
-
- First sequence
|
867 |
-
- Second and third sequences are follow-ups (no subject line needed)
|
868 |
- All emails should be HTML formatted with proper <br> tags
|
869 |
- Professional but friendly tone
|
870 |
- Include clear call-to-actions
|
871 |
- Focus on building consent and trust
|
872 |
-
|
873 |
Respond with ONLY a JSON object containing the email content:
|
874 |
-
|
875 |
-
|
876 |
-
"subject": "Subject line for variant A",
|
877 |
-
"body": "HTML formatted email body for variant A"
|
878 |
-
},
|
879 |
-
"sequence1_variant_b": {
|
880 |
-
"subject": "Subject line for variant B",
|
881 |
-
"body": "HTML formatted email body for variant B"
|
882 |
-
},
|
883 |
-
"sequence2": {
|
884 |
-
"body": "HTML formatted email body for outreach"
|
885 |
-
},
|
886 |
-
"sequence3": {
|
887 |
-
"body": "HTML formatted email body for follow-up"
|
888 |
-
}
|
889 |
-
}
|
890 |
-
|
891 |
IMPORTANT: Respond with ONLY valid JSON. No additional text."""
|
892 |
|
893 |
prompt_template = ChatPromptTemplate.from_messages([
|
@@ -896,19 +894,121 @@ async def generate_sequences_with_llm(job_description: str) -> List[CampaignSequ
|
|
896 |
])
|
897 |
|
898 |
messages = prompt_template.format_messages(job_description=job_description)
|
899 |
-
response = await
|
900 |
|
901 |
try:
|
902 |
-
|
|
|
903 |
|
904 |
-
|
905 |
-
|
906 |
-
|
907 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
908 |
|
909 |
-
content = content.strip()
|
910 |
-
parsed_data = json.loads(content)
|
911 |
-
sequences = create_sequences_from_content(parsed_data, job_description)
|
912 |
return sequences
|
913 |
|
914 |
except Exception as parse_error:
|
|
|
576 |
return {
|
577 |
"ok": True,
|
578 |
"message": "Sequences generated and saved successfully",
|
579 |
+
"generated_sequences": [seq for seq in generated_sequences],
|
580 |
"save_result": result
|
581 |
}
|
582 |
|
|
|
836 |
}
|
837 |
|
838 |
async def generate_sequences_with_llm(job_description: str) -> List[CampaignSequence]:
|
839 |
+
class email_seq(BaseModel):
|
840 |
+
subject: str = Field(description="Subject line for the email")
|
841 |
+
body: str = Field(description="Body of the email")
|
842 |
+
class structure(BaseModel):
|
843 |
+
introduction: email_seq = Field(description="Email sequence for sequence 1 asking for consent and interest in the role")
|
844 |
+
email_sequence_2: email_seq = Field(description="Email sequence for sequence 2 following up on updates and next steps")
|
845 |
+
email_sequence_3: email_seq = Field(description="Email sequence for sequence 3 Another variant on following up on updates and next steps")
|
846 |
+
|
847 |
+
|
848 |
+
|
849 |
"""Generate email sequences using LangChain and OpenAI based on job description"""
|
850 |
|
851 |
if not LANGCHAIN_AVAILABLE:
|
|
|
858 |
return await generate_template_sequences(job_description)
|
859 |
|
860 |
llm = ChatOpenAI(
|
861 |
+
model="gpt-4o",
|
862 |
temperature=0.7,
|
863 |
openai_api_key=openai_api_key
|
864 |
)
|
865 |
+
str_llm = llm.with_structured_output(structure)
|
866 |
|
867 |
+
system_prompt = """You are an expert email sequence template generator for recruitment campaigns.
|
868 |
+
|
869 |
Generate ONLY the subject lines and email body content for 3 professional email sequences.
|
870 |
+
Write the email on behalf of Ali Taghikhani, CEO SRN
|
871 |
+
In the templates use placeholders like, welcome_message, closing message, first_name, company,title only.
|
872 |
+
|
873 |
Email Sequence Structure:
|
874 |
+
1. INTRODUCTION (Day 1): Ask for consent and interest in the role, In the starting use the welcome message placeholder, and in the end use closing message template along with the name and title of sender.placeholders must be used like this {{first_name}}
|
875 |
+
2. OUTREACH (Day 3): Provide detailed job information
|
876 |
3. FOLLOW-UP (Day 5): Follow up on updates and next steps
|
877 |
+
|
878 |
Requirements:
|
879 |
+
- First sequence will only ask about the consent and interest in the role, no other information is needed.
|
880 |
+
- Second and third sequences are follow-ups (no subject line needed) in the 3rd sequence try providing some information about the role and the company.
|
881 |
- All emails should be HTML formatted with proper <br> tags
|
882 |
- Professional but friendly tone
|
883 |
- Include clear call-to-actions
|
884 |
- Focus on building consent and trust
|
885 |
+
|
886 |
Respond with ONLY a JSON object containing the email content:
|
887 |
+
|
888 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
889 |
IMPORTANT: Respond with ONLY valid JSON. No additional text."""
|
890 |
|
891 |
prompt_template = ChatPromptTemplate.from_messages([
|
|
|
894 |
])
|
895 |
|
896 |
messages = prompt_template.format_messages(job_description=job_description)
|
897 |
+
response = await str_llm.ainvoke(messages)
|
898 |
|
899 |
try:
|
900 |
+
# Handle the response structure correctly
|
901 |
+
sequences = []
|
902 |
|
903 |
+
# Sequence 1: Introduction with A/B variants
|
904 |
+
if hasattr(response, 'introduction') and response.introduction:
|
905 |
+
# Check if body is a string or dict
|
906 |
+
intro_body = response.introduction.body
|
907 |
+
if isinstance(intro_body, dict):
|
908 |
+
# If it's a dict, extract the content
|
909 |
+
intro_body = str(intro_body)
|
910 |
+
|
911 |
+
sequences.append(CampaignSequence(
|
912 |
+
seq_number=1,
|
913 |
+
seq_delay_details=SeqDelayDetails(delay_in_days=1),
|
914 |
+
seq_variants=[
|
915 |
+
SeqVariant(
|
916 |
+
subject=response.introduction.subject,
|
917 |
+
email_body=intro_body,
|
918 |
+
variant_label="A"
|
919 |
+
)
|
920 |
+
]
|
921 |
+
))
|
922 |
+
|
923 |
+
# Sequence 2: Outreach
|
924 |
+
if hasattr(response, 'email_sequence_2') and response.email_sequence_2:
|
925 |
+
seq2_body = response.email_sequence_2.body
|
926 |
+
if isinstance(seq2_body, dict):
|
927 |
+
seq2_body = str(seq2_body)
|
928 |
+
|
929 |
+
sequences.append(CampaignSequence(
|
930 |
+
seq_number=2,
|
931 |
+
seq_delay_details=SeqDelayDetails(delay_in_days=3),
|
932 |
+
subject="",
|
933 |
+
email_body=seq2_body
|
934 |
+
))
|
935 |
+
|
936 |
+
# Sequence 3: Follow-up
|
937 |
+
if hasattr(response, 'email_sequence_3') and response.email_sequence_3:
|
938 |
+
seq3_body = response.email_sequence_3.body
|
939 |
+
if isinstance(seq3_body, dict):
|
940 |
+
seq3_body = str(seq3_body)
|
941 |
+
|
942 |
+
sequences.append(CampaignSequence(
|
943 |
+
seq_number=3,
|
944 |
+
seq_delay_details=SeqDelayDetails(delay_in_days=5),
|
945 |
+
subject="",
|
946 |
+
email_body=seq3_body
|
947 |
+
))
|
948 |
+
|
949 |
+
# Fill with templates if needed
|
950 |
+
while len(sequences) < 3:
|
951 |
+
if len(sequences) == 0:
|
952 |
+
sequences.append(CampaignSequence(
|
953 |
+
seq_number=1,
|
954 |
+
seq_delay_details=SeqDelayDetails(delay_in_days=1),
|
955 |
+
seq_variants=[
|
956 |
+
SeqVariant(
|
957 |
+
subject=f"Quick question about {job_description}",
|
958 |
+
email_body=f"""<p>Hi there,<br><br>
|
959 |
+
I came across your profile and noticed your experience in {job_description}.
|
960 |
+
I'm reaching out because we have some exciting opportunities that might be a great fit for your background.<br><br>
|
961 |
+
Before I share more details, I wanted to ask: Are you currently open to exploring new opportunities in this space?<br><br>
|
962 |
+
Would you be interested in hearing more about the roles we have available?<br><br>
|
963 |
+
Best regards,<br>
|
964 |
+
[Your Name]</p>""",
|
965 |
+
variant_label="A"
|
966 |
+
),
|
967 |
+
SeqVariant(
|
968 |
+
subject=f"Interested in {job_description} opportunities?",
|
969 |
+
email_body=f"""<p>Hello,<br><br>
|
970 |
+
I hope this message finds you well. I'm a recruiter specializing in {job_description} positions.<br><br>
|
971 |
+
I'd love to connect and share some opportunities that align with your expertise.
|
972 |
+
Are you currently open to exploring new roles in this space?<br><br>
|
973 |
+
If so, I can send you specific details about the positions we have available.<br><br>
|
974 |
+
Thanks,<br>
|
975 |
+
[Your Name]</p>""",
|
976 |
+
variant_label="B"
|
977 |
+
)
|
978 |
+
]
|
979 |
+
))
|
980 |
+
elif len(sequences) == 1:
|
981 |
+
sequences.append(CampaignSequence(
|
982 |
+
seq_number=2,
|
983 |
+
seq_delay_details=SeqDelayDetails(delay_in_days=3),
|
984 |
+
subject="",
|
985 |
+
email_body=f"""<p>Hi,<br><br>
|
986 |
+
Thanks for your interest! Here are more details about the {job_description} opportunities:<br><br>
|
987 |
+
<strong>Role Details:</strong><br>
|
988 |
+
• [Specific responsibilities]<br>
|
989 |
+
• [Required skills and experience]<br>
|
990 |
+
• [Team and company information]<br><br>
|
991 |
+
<strong>Benefits:</strong><br>
|
992 |
+
• [Compensation and benefits]<br>
|
993 |
+
• [Growth opportunities]<br>
|
994 |
+
• [Work environment]<br><br>
|
995 |
+
Would you be interested in a quick call to discuss this role in more detail?<br><br>
|
996 |
+
Best regards,<br>
|
997 |
+
[Your Name]</p>"""
|
998 |
+
))
|
999 |
+
elif len(sequences) == 2:
|
1000 |
+
sequences.append(CampaignSequence(
|
1001 |
+
seq_number=3,
|
1002 |
+
seq_delay_details=SeqDelayDetails(delay_in_days=5),
|
1003 |
+
subject="",
|
1004 |
+
email_body=f"""<p>Hi,<br><br>
|
1005 |
+
Just wanted to follow up on the {job_description} opportunity I shared.<br><br>
|
1006 |
+
Have you had a chance to review the information? I'd love to hear your thoughts and answer any questions.<br><br>
|
1007 |
+
If you're interested, I can help schedule next steps. If not, no worries at all!<br><br>
|
1008 |
+
Thanks for your time!<br>
|
1009 |
+
[Your Name]</p>"""
|
1010 |
+
))
|
1011 |
|
|
|
|
|
|
|
1012 |
return sequences
|
1013 |
|
1014 |
except Exception as parse_error:
|