fusesell 1.3.42__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. fusesell-1.3.42.dist-info/METADATA +873 -0
  2. fusesell-1.3.42.dist-info/RECORD +35 -0
  3. fusesell-1.3.42.dist-info/WHEEL +5 -0
  4. fusesell-1.3.42.dist-info/entry_points.txt +2 -0
  5. fusesell-1.3.42.dist-info/licenses/LICENSE +21 -0
  6. fusesell-1.3.42.dist-info/top_level.txt +2 -0
  7. fusesell.py +20 -0
  8. fusesell_local/__init__.py +37 -0
  9. fusesell_local/api.py +343 -0
  10. fusesell_local/cli.py +1480 -0
  11. fusesell_local/config/__init__.py +11 -0
  12. fusesell_local/config/default_email_templates.json +34 -0
  13. fusesell_local/config/default_prompts.json +19 -0
  14. fusesell_local/config/default_scoring_criteria.json +154 -0
  15. fusesell_local/config/prompts.py +245 -0
  16. fusesell_local/config/settings.py +277 -0
  17. fusesell_local/pipeline.py +978 -0
  18. fusesell_local/stages/__init__.py +19 -0
  19. fusesell_local/stages/base_stage.py +603 -0
  20. fusesell_local/stages/data_acquisition.py +1820 -0
  21. fusesell_local/stages/data_preparation.py +1238 -0
  22. fusesell_local/stages/follow_up.py +1728 -0
  23. fusesell_local/stages/initial_outreach.py +2972 -0
  24. fusesell_local/stages/lead_scoring.py +1452 -0
  25. fusesell_local/utils/__init__.py +36 -0
  26. fusesell_local/utils/agent_context.py +552 -0
  27. fusesell_local/utils/auto_setup.py +361 -0
  28. fusesell_local/utils/birthday_email_manager.py +467 -0
  29. fusesell_local/utils/data_manager.py +4857 -0
  30. fusesell_local/utils/event_scheduler.py +959 -0
  31. fusesell_local/utils/llm_client.py +342 -0
  32. fusesell_local/utils/logger.py +203 -0
  33. fusesell_local/utils/output_helpers.py +2443 -0
  34. fusesell_local/utils/timezone_detector.py +914 -0
  35. fusesell_local/utils/validators.py +436 -0
@@ -0,0 +1,11 @@
1
+ """
2
+ FuseSell Configuration - Configuration management and templates
3
+ """
4
+
5
+ from .prompts import PromptManager
6
+ from .settings import ConfigManager
7
+
8
+ __all__ = [
9
+ 'PromptManager',
10
+ 'ConfigManager'
11
+ ]
@@ -0,0 +1,34 @@
1
+ {
2
+ "initial_outreach": {
3
+ "formal": {
4
+ "greeting": "Dear {recipient_name},",
5
+ "introduction": "I hope this email finds you well. My name is {staff_name} from {org_name}.",
6
+ "value_proposition": "I noticed that {company_name} operates in the {industry} sector, and I believe our solution could help address some of the challenges you might be facing.",
7
+ "call_to_action": "Would you be interested in a brief conversation to explore how we might be able to help?",
8
+ "closing": "Best regards,\n{staff_name}\n{org_name}"
9
+ },
10
+ "casual": {
11
+ "greeting": "Hi {recipient_name},",
12
+ "introduction": "I'm {staff_name} from {org_name}, and I've been following {company_name}'s work in {industry}.",
13
+ "value_proposition": "I think there might be some interesting opportunities for us to collaborate and help you tackle {pain_point}.",
14
+ "call_to_action": "Would you be up for a quick chat to explore this further?",
15
+ "closing": "Cheers,\n{staff_name}"
16
+ }
17
+ },
18
+ "follow_up": {
19
+ "no_response": {
20
+ "greeting": "Hi {recipient_name},",
21
+ "reference": "I wanted to follow up on my previous email about {topic}.",
22
+ "value_add": "I thought you might find this relevant: {additional_info}",
23
+ "call_to_action": "If now isn't the right time, I completely understand. Would a different time work better?",
24
+ "closing": "Best,\n{staff_name}"
25
+ },
26
+ "engaged": {
27
+ "greeting": "Hi {recipient_name},",
28
+ "reference": "Thanks for your interest in our previous conversation about {topic}.",
29
+ "next_step": "As discussed, I've prepared some additional information that might be helpful.",
30
+ "call_to_action": "When would be a good time for a more detailed discussion?",
31
+ "closing": "Looking forward to hearing from you,\n{staff_name}"
32
+ }
33
+ }
34
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "data_preparation": {
3
+ "extract_company_info": "Analyze the following raw customer data and extract structured company information including name, industry, size, address, and key business details. Format the response as JSON with the following structure: {\"companyInfo\": {\"name\": \"\", \"industry\": \"\", \"size\": \"\", \"address\": \"\", \"website\": \"\", \"annualRevenue\": \"\"}}",
4
+ "identify_pain_points": "Based on the company information provided, identify potential business pain points and challenges. Categorize each pain point by impact area and provide detailed descriptions. Format as JSON array: [{\"category\": \"\", \"impact\": \"\", \"description\": \"\"}]",
5
+ "financial_analysis": "Analyze the financial indicators and company size to estimate revenue, growth potential, and funding sources. Provide structured financial assessment."
6
+ },
7
+ "lead_scoring": {
8
+ "evaluate_fit": "Evaluate how well this customer profile fits our product criteria. Consider company size, industry, pain points, and technology needs. Provide detailed scoring breakdown.",
9
+ "score_calculation": "Calculate a lead score from 0-100 based on the provided criteria and customer data. Show detailed breakdown of scoring factors."
10
+ },
11
+ "initial_outreach": {
12
+ "email_generation": "Create four initial outreach ##action_type## variations in ##language## for ##customer_name## of ##company_name## on behalf of ##staff_name## (sender, from ##org_name##). Guidelines:\n\n1. Use email tones from this list: Professional, Friendly, Urgent, Persuasive, Apologetic, Appreciative, Informative, Collaborative, Assertive, Empathetic. You may use up to two tones if appropriate.\n\n2. For each email variation:\n- Craft an intriguing subject line (max 50 characters) hinting at specific value.\n- Use ONLY recipient's first name in greeting. ##first_name_guide##. For languages other than Vietnamese: Use only the recipient's first name without honorifics. \n- Open with a personalized introduction, including sender's name, position, and company.\n- Demonstrate knowledge of the recipient's company and industry.\n- Establish relevance by mentioning specific challenges or opportunities in their industry.\n- Present a concise value proposition addressing top 2-3 pain points.\n- Highlight key benefits aligned with recipient's needs.\n- Include relevant social proof, mentioning other clients in the same industry.\n- Provide 3-5 bullet points of specific solutions or features tailored to the recipient's needs.\n- Use one of these call-to-action approaches:\na. Simple Interest Check: Ask for a minimal, low-effort reply to express interest\nb. Challenge Inquiry: Invite sharing of specific challenges with an easy reply option\nc. Quick Call Proposal: Suggest a brief, no-pressure call\nd. Specific Question: Ask a relevant, thought-provoking question about their business\ne. Resource Offer: Propose sending a valuable resource (case study, whitepaper, etc.)\n- Include an appropriate closing phrase that matches the email's tone and content, followed by a signature with this format:\n<p>[Closing phrase],\n##staff_name##\n##org_name##</p>\n\n3. Product Mentioning:\n- For half of the variations, explicitly mention the product name and key features.\n- The product to mention should be ##selected_product## (mention this exact name) if provided, or the top-scoring product based on lead_scoring results.\n- For the other half, focus on benefits and pain points without directly naming the product.\n\n4. Use simple HTML formatting (<p>, <strong>, <ul>, <li>) for readability and structure.\n5. Use clear paragraph breaks and bullet points to enhance scannability.\n6. Don't use placeholder text or invent information.\n7. Align with company brand voice and recipient's needs/industry.\n8. Aim for 300-400 words for each email body.\n9. Do not include any hyperlinks or attachments in the email body.\n9. The pronoun must be consistent throughout the draft. Pronoun usage: \n- For Vietnamese language: If pronoun refers to staff name, use ##staff_name## throughout the email\n- For all other languages: Use appropriate first-person pronouns based on the language\n10. DON'T hallucinate, don't make up information. \n\nEnsure diversity across the four variations:\n- Use at least 2 different email tones\n- Use at least 1 different approach methods\n- 2 variations should mention the product name, 2 should not\n\nCreate all 4 variations first, then randomly shuffle their order before assigning priority orders. Return 4 objects in this JSON format:\n[\n{\n\"mail_tone\": \"Recommended tone\",\n\"subject\": \"Subject line\",\n\"body\": \"HTML-formatted email body including signature\",\n\"priority_order\": 1,\n\"approach\": \"Approach method used\",\n\"product_mention\": true/false,\n\"product_name\": \"##selected_product## or top-scoring product if mentioned, else null\",\n\"message_type\": \"Email\",\n\"tags\": [\"Email\", \"MailTone1\", \"MailTone2\", \"ApproachMethod\", \"ProductMentioned/ProductNotMentioned\"]\n}\n]\n\nThe objects MUST be in a random order, not sorted by mail_tone or any other field. Assign priority_order from 1 to 4 AFTER shuffling.\n\nNO REDUNDANT WORDS. NO NEED TO BE WRAPPED IN ``` CODE BLOCK CHARACTERS.\nInput information: ##company_info##\nSelected product information: ##selected_product_info##\n",
13
+ "subject_line": "Create 3 compelling email subject lines for this outreach email that are personalized to the customer and likely to generate opens."
14
+ },
15
+ "follow_up": {
16
+ "analyze_interaction": "Analyze the previous interaction history and determine the best follow-up approach based on customer engagement level and response patterns.",
17
+ "generate_followup": "Create a follow-up {{$node[\"Webhook\"].json.body.interaction_type}} draft in {{$node[\"Webhook\"].json.body.language}} for ##customer_name## from ##staff_name##. Guidelines:\n\n1. Choose tone: Friendly, Informative, or Collaborative.\n\n2. Draft components:\n - Subject line (max 50 chars): Reference previous outreach without mentioning dates\n - Greeting: Use recipient's first name only. ##first_name_guide##\n - Opening: Briefly acknowledge previous contact without specific date\n - Body: \n - Subtly reiterate key value proposition (focus on 1-2 pain points from ##pain_points##)\n - Add new, relevant information based on ##companyInfo##, ##currentTechStack##, or ##developmentPlans##\n - Approach: Gentle Reminder with Additional Value\n - Clear, low-pressure call-to-action (e.g., offer to schedule a brief call or provide more information)\n - Closing: Match email tone\n - Signature: ##staff_name##<br>##org_name##\n\n3. Product mention: If appropriate, reference ##selected_product## or top-scoring product from ##lead_scoring## indirectly by focusing on benefits/solutions\n\n4. Formatting: Simple HTML (<p>, <strong>). No complex HTML, links, or attachments.\n\n5. Style: Align with ##org_name## brand voice and recipient's industry (##companyInfo.industry##). Max 125 words.\n\n6. Don't use placeholder text or invent. information.\n\n7. Don't include any hyperlinks or attachments in the email body.\n\n8. The pronoun must be consistent throughout the draft.\n\n9. DON'T hallucinate, don't make up information. \n\nReturn JSON:\n{\n \"mail_tone\": \"Chosen tone\",\n \"subject\": \"Subject line\",\n \"body\": \"HTML-formatted email body with signature\",\n \"approach\": \"Gentle Reminder with Additional Value\",\n \"product_mention\": true/false,\n \"product_name\": \"Product name if mentioned indirectly, else null\",\n \"message_type\": \"Follow-up Email\",\n \"tags\": [\"Follow-up\", \"MailTone\", \"GentleReminder\", \"AdditionalValue\", \"ProductMentioned/ProductNotMentioned\"]\n}\n\nNO REDUNDANT WORDS. NO NEED TO BE WRAPPED IN ``` CODE BLOCK CHARACTERS.\n\nInput: ##companyInfo##\nProduct info: ##selected_product_info##\nPrevious outreach: ##last_interaction_date##\nSales stage: ##current_sales_stage##\nFollow-up reason: ##follow_up_reason##\nInitial email content: ##sent_content##\n"
18
+ }
19
+ }
@@ -0,0 +1,154 @@
1
+ {
2
+ "scoring_weights": {
3
+ "company_size": 0.25,
4
+ "industry_fit": 0.2,
5
+ "pain_points_match": 0.3,
6
+ "financial_health": 0.15,
7
+ "technology_readiness": 0.1
8
+ },
9
+ "company_size_scores": {
10
+ "startup": 60,
11
+ "small": 70,
12
+ "medium": 85,
13
+ "large": 90,
14
+ "enterprise": 95
15
+ },
16
+ "industry_multipliers": {
17
+ "technology": 1.2,
18
+ "finance": 1.1,
19
+ "healthcare": 1.0,
20
+ "manufacturing": 0.9,
21
+ "retail": 0.8
22
+ },
23
+ "pain_point_categories": {
24
+ "efficiency": 20,
25
+ "cost_reduction": 25,
26
+ "growth": 30,
27
+ "compliance": 15,
28
+ "customer_experience": 20
29
+ },
30
+ "rta": [
31
+ {
32
+ "name": "industry_fit",
33
+ "weight": 25,
34
+ "definition": "How well the customer's industry aligns with the product's target market",
35
+ "guidelines": "Score based on industry match and market presence",
36
+ "scoring_factors": [
37
+ "Perfect industry match: 80-100",
38
+ "Related industry: 60-79",
39
+ "Adjacent industry: 40-59",
40
+ "Unrelated industry: 0-39"
41
+ ]
42
+ },
43
+ {
44
+ "name": "company_size",
45
+ "weight": 20,
46
+ "definition": "Company size alignment with product's ideal customer profile",
47
+ "guidelines": "Score based on employee count and revenue alignment",
48
+ "scoring_factors": [
49
+ "Ideal size range: 80-100",
50
+ "Close to ideal: 60-79",
51
+ "Acceptable size: 40-59",
52
+ "Poor size fit: 0-39"
53
+ ]
54
+ },
55
+ {
56
+ "name": "pain_points",
57
+ "weight": 30,
58
+ "definition": "How well the product addresses customer's identified pain points",
59
+ "guidelines": "Score based on pain point alignment and severity",
60
+ "scoring_factors": [
61
+ "Addresses all major pain points: 80-100",
62
+ "Addresses most pain points: 60-79",
63
+ "Addresses some pain points: 40-59",
64
+ "Addresses few/no pain points: 0-39"
65
+ ]
66
+ },
67
+ {
68
+ "name": "product_fit",
69
+ "weight": 15,
70
+ "definition": "Overall product-customer compatibility",
71
+ "guidelines": "Score based on feature alignment and use case match",
72
+ "scoring_factors": [
73
+ "Excellent feature match: 80-100",
74
+ "Good feature match: 60-79",
75
+ "Basic feature match: 40-59",
76
+ "Poor feature match: 0-39"
77
+ ]
78
+ },
79
+ {
80
+ "name": "geographic_market_fit",
81
+ "weight": 10,
82
+ "definition": "Geographic alignment between customer location and product availability",
83
+ "guidelines": "Score based on market presence and localization",
84
+ "scoring_factors": [
85
+ "Strong market presence: 80-100",
86
+ "Moderate presence: 60-79",
87
+ "Limited presence: 40-59",
88
+ "No market presence: 0-39"
89
+ ]
90
+ }
91
+ ],
92
+ "rta_enterprise": [
93
+ {
94
+ "name": "budget_authority",
95
+ "weight": 25,
96
+ "definition": "Customer's budget authority and financial capacity",
97
+ "guidelines": "Score based on budget size and decision-making authority",
98
+ "scoring_factors": [
99
+ "Confirmed budget >$100k: 90-100",
100
+ "Estimated budget $50k-$100k: 70-89",
101
+ "Estimated budget $10k-$50k: 40-69",
102
+ "No budget information: 0-39"
103
+ ]
104
+ },
105
+ {
106
+ "name": "technical_fit",
107
+ "weight": 20,
108
+ "definition": "Technical requirements alignment with product capabilities",
109
+ "guidelines": "Score based on technical complexity and integration needs",
110
+ "scoring_factors": [
111
+ "Perfect technical match: 80-100",
112
+ "Good match with minor customization: 60-79",
113
+ "Moderate match requiring development: 40-59",
114
+ "Poor technical fit: 0-39"
115
+ ]
116
+ },
117
+ {
118
+ "name": "decision_timeline",
119
+ "weight": 15,
120
+ "definition": "Customer's decision-making timeline and urgency",
121
+ "guidelines": "Score based on purchase timeline and urgency indicators",
122
+ "scoring_factors": [
123
+ "Immediate need (0-3 months): 80-100",
124
+ "Near-term need (3-6 months): 60-79",
125
+ "Future need (6-12 months): 40-59",
126
+ "No defined timeline: 0-39"
127
+ ]
128
+ },
129
+ {
130
+ "name": "competitive_situation",
131
+ "weight": 20,
132
+ "definition": "Competitive landscape and our positioning",
133
+ "guidelines": "Score based on competitive threats and our advantages",
134
+ "scoring_factors": [
135
+ "No competition, clear leader: 80-100",
136
+ "Preferred vendor, light competition: 60-79",
137
+ "Competitive situation, even playing field: 40-59",
138
+ "Strong competition, uphill battle: 0-39"
139
+ ]
140
+ },
141
+ {
142
+ "name": "stakeholder_engagement",
143
+ "weight": 20,
144
+ "definition": "Level of stakeholder engagement and champion strength",
145
+ "guidelines": "Score based on champion strength and stakeholder buy-in",
146
+ "scoring_factors": [
147
+ "Strong champion with executive support: 80-100",
148
+ "Good champion with some support: 60-79",
149
+ "Weak champion or mixed support: 40-59",
150
+ "No champion or opposition: 0-39"
151
+ ]
152
+ }
153
+ ]
154
+ }
@@ -0,0 +1,245 @@
1
+ """
2
+ Prompt Manager for FuseSell Local
3
+ Handles LLM prompt templates and variable substitution
4
+ """
5
+
6
+ from typing import Dict, Any, Optional
7
+ import re
8
+ import logging
9
+ from .settings import ConfigManager
10
+
11
+
12
+ class PromptManager:
13
+ """
14
+ Manages LLM prompts with template substitution and customization.
15
+ """
16
+
17
+ def __init__(self, config_manager: ConfigManager):
18
+ """
19
+ Initialize prompt manager.
20
+
21
+ Args:
22
+ config_manager: Configuration manager instance
23
+ """
24
+ self.config_manager = config_manager
25
+ self.logger = logging.getLogger("fusesell.prompts")
26
+
27
+ def get_prompt(
28
+ self,
29
+ stage: str,
30
+ prompt_key: str,
31
+ variables: Optional[Dict[str, Any]] = None,
32
+ team_id: Optional[str] = None,
33
+ language: str = "english"
34
+ ) -> str:
35
+ """
36
+ Get formatted prompt for a specific stage and key.
37
+
38
+ Args:
39
+ stage: Pipeline stage name
40
+ prompt_key: Specific prompt identifier
41
+ variables: Variables for template substitution
42
+ team_id: Optional team ID for customization
43
+ language: Language for prompts
44
+
45
+ Returns:
46
+ Formatted prompt string
47
+ """
48
+ try:
49
+ # Get prompts configuration
50
+ prompts = self.config_manager.get_prompts(team_id, language)
51
+
52
+ # Get stage-specific prompts
53
+ stage_prompts = prompts.get(stage, {})
54
+ if not stage_prompts:
55
+ self.logger.warning(f"No prompts found for stage: {stage}")
56
+ return ""
57
+
58
+ # Get specific prompt template
59
+ prompt_template = stage_prompts.get(prompt_key, "")
60
+ if not prompt_template:
61
+ self.logger.warning(f"No prompt found for {stage}.{prompt_key}")
62
+ return ""
63
+
64
+ # Apply variable substitution
65
+ if variables:
66
+ prompt_template = self._substitute_variables(prompt_template, variables)
67
+
68
+ return prompt_template
69
+
70
+ except Exception as e:
71
+ self.logger.error(f"Failed to get prompt {stage}.{prompt_key}: {str(e)}")
72
+ return ""
73
+
74
+ def get_data_acquisition_prompts(self, variables: Dict[str, Any], **kwargs) -> Dict[str, str]:
75
+ """Get all prompts for data acquisition stage."""
76
+ return {
77
+ 'website_extraction': self.get_prompt('data_acquisition', 'website_extraction', variables, **kwargs),
78
+ 'business_card_ocr': self.get_prompt('data_acquisition', 'business_card_ocr', variables, **kwargs),
79
+ 'social_media_analysis': self.get_prompt('data_acquisition', 'social_media_analysis', variables, **kwargs),
80
+ 'data_consolidation': self.get_prompt('data_acquisition', 'data_consolidation', variables, **kwargs)
81
+ }
82
+
83
+ def get_data_preparation_prompts(self, variables: Dict[str, Any], **kwargs) -> Dict[str, str]:
84
+ """Get all prompts for data preparation stage."""
85
+ return {
86
+ 'extract_company_info': self.get_prompt('data_preparation', 'extract_company_info', variables, **kwargs),
87
+ 'identify_pain_points': self.get_prompt('data_preparation', 'identify_pain_points', variables, **kwargs),
88
+ 'financial_analysis': self.get_prompt('data_preparation', 'financial_analysis', variables, **kwargs),
89
+ 'development_plans': self.get_prompt('data_preparation', 'development_plans', variables, **kwargs),
90
+ 'technology_analysis': self.get_prompt('data_preparation', 'technology_analysis', variables, **kwargs)
91
+ }
92
+
93
+ def get_lead_scoring_prompts(self, variables: Dict[str, Any], **kwargs) -> Dict[str, str]:
94
+ """Get all prompts for lead scoring stage."""
95
+ return {
96
+ 'evaluate_fit': self.get_prompt('lead_scoring', 'evaluate_fit', variables, **kwargs),
97
+ 'score_calculation': self.get_prompt('lead_scoring', 'score_calculation', variables, **kwargs),
98
+ 'competitive_analysis': self.get_prompt('lead_scoring', 'competitive_analysis', variables, **kwargs),
99
+ 'recommendation': self.get_prompt('lead_scoring', 'recommendation', variables, **kwargs)
100
+ }
101
+
102
+ def get_initial_outreach_prompts(self, variables: Dict[str, Any], **kwargs) -> Dict[str, str]:
103
+ """Get all prompts for initial outreach stage."""
104
+ return {
105
+ 'email_generation': self.get_prompt('initial_outreach', 'email_generation', variables, **kwargs),
106
+ 'subject_line': self.get_prompt('initial_outreach', 'subject_line', variables, **kwargs),
107
+ 'tone_adjustment': self.get_prompt('initial_outreach', 'tone_adjustment', variables, **kwargs),
108
+ 'personalization': self.get_prompt('initial_outreach', 'personalization', variables, **kwargs)
109
+ }
110
+
111
+ def get_follow_up_prompts(self, variables: Dict[str, Any], **kwargs) -> Dict[str, str]:
112
+ """Get all prompts for follow-up stage."""
113
+ return {
114
+ 'analyze_interaction': self.get_prompt('follow_up', 'analyze_interaction', variables, **kwargs),
115
+ 'generate_followup': self.get_prompt('follow_up', 'generate_followup', variables, **kwargs),
116
+ 'timing_analysis': self.get_prompt('follow_up', 'timing_analysis', variables, **kwargs),
117
+ 'sequence_planning': self.get_prompt('follow_up', 'sequence_planning', variables, **kwargs)
118
+ }
119
+
120
+ def _substitute_variables(self, template: str, variables: Dict[str, Any]) -> str:
121
+ """
122
+ Substitute variables in prompt template.
123
+
124
+ Args:
125
+ template: Prompt template with placeholders
126
+ variables: Variables to substitute
127
+
128
+ Returns:
129
+ Template with variables substituted
130
+ """
131
+ try:
132
+ # Handle nested dictionary access (e.g., {customer.name})
133
+ def replace_nested(match):
134
+ var_path = match.group(1)
135
+ value = self._get_nested_value(variables, var_path)
136
+ return str(value) if value is not None else f"{{{var_path}}}"
137
+
138
+ # Replace {variable} and {nested.variable} patterns
139
+ result = re.sub(r'\{([^}]+)\}', replace_nested, template)
140
+
141
+ return result
142
+
143
+ except Exception as e:
144
+ self.logger.warning(f"Variable substitution failed: {str(e)}")
145
+ return template
146
+
147
+ def _get_nested_value(self, data: Dict[str, Any], path: str) -> Any:
148
+ """
149
+ Get value from nested dictionary using dot notation.
150
+
151
+ Args:
152
+ data: Dictionary to search
153
+ path: Dot-separated path (e.g., 'customer.name')
154
+
155
+ Returns:
156
+ Value at path or None if not found
157
+ """
158
+ try:
159
+ keys = path.split('.')
160
+ value = data
161
+
162
+ for key in keys:
163
+ if isinstance(value, dict) and key in value:
164
+ value = value[key]
165
+ else:
166
+ return None
167
+
168
+ return value
169
+
170
+ except Exception:
171
+ return None
172
+
173
+ def validate_prompt_variables(self, template: str, variables: Dict[str, Any]) -> Dict[str, Any]:
174
+ """
175
+ Validate that all required variables are provided for a template.
176
+
177
+ Args:
178
+ template: Prompt template
179
+ variables: Available variables
180
+
181
+ Returns:
182
+ Dictionary with validation results
183
+ """
184
+ # Find all variable placeholders
185
+ placeholders = re.findall(r'\{([^}]+)\}', template)
186
+
187
+ missing_vars = []
188
+ available_vars = []
189
+
190
+ for placeholder in placeholders:
191
+ value = self._get_nested_value(variables, placeholder)
192
+ if value is None:
193
+ missing_vars.append(placeholder)
194
+ else:
195
+ available_vars.append(placeholder)
196
+
197
+ return {
198
+ 'valid': len(missing_vars) == 0,
199
+ 'missing_variables': missing_vars,
200
+ 'available_variables': available_vars,
201
+ 'total_placeholders': len(placeholders)
202
+ }
203
+
204
+ def create_variable_context(self,
205
+ customer_data: Optional[Dict[str, Any]] = None,
206
+ lead_scores: Optional[Dict[str, Any]] = None,
207
+ org_info: Optional[Dict[str, Any]] = None,
208
+ team_info: Optional[Dict[str, Any]] = None,
209
+ **additional_vars) -> Dict[str, Any]:
210
+ """
211
+ Create a comprehensive variable context for prompt substitution.
212
+
213
+ Args:
214
+ customer_data: Customer information
215
+ lead_scores: Lead scoring results
216
+ org_info: Organization information
217
+ team_info: Team information
218
+ **additional_vars: Additional variables
219
+
220
+ Returns:
221
+ Complete variable context
222
+ """
223
+ context = {}
224
+
225
+ if customer_data:
226
+ context['customer'] = customer_data
227
+ context['company'] = customer_data.get('companyInfo', {})
228
+ context['contact'] = customer_data.get('primaryContact', {})
229
+ context['pain_points'] = customer_data.get('painPoints', [])
230
+
231
+ if lead_scores:
232
+ context['lead_scores'] = lead_scores
233
+ context['top_score'] = max(lead_scores.get('scores', []), key=lambda x: x.get('score', 0), default={})
234
+
235
+ if org_info:
236
+ context['org'] = org_info
237
+ context['seller'] = org_info
238
+
239
+ if team_info:
240
+ context['team'] = team_info
241
+
242
+ # Add additional variables
243
+ context.update(additional_vars)
244
+
245
+ return context