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.
- fusesell-1.3.42.dist-info/METADATA +873 -0
- fusesell-1.3.42.dist-info/RECORD +35 -0
- fusesell-1.3.42.dist-info/WHEEL +5 -0
- fusesell-1.3.42.dist-info/entry_points.txt +2 -0
- fusesell-1.3.42.dist-info/licenses/LICENSE +21 -0
- fusesell-1.3.42.dist-info/top_level.txt +2 -0
- fusesell.py +20 -0
- fusesell_local/__init__.py +37 -0
- fusesell_local/api.py +343 -0
- fusesell_local/cli.py +1480 -0
- fusesell_local/config/__init__.py +11 -0
- fusesell_local/config/default_email_templates.json +34 -0
- fusesell_local/config/default_prompts.json +19 -0
- fusesell_local/config/default_scoring_criteria.json +154 -0
- fusesell_local/config/prompts.py +245 -0
- fusesell_local/config/settings.py +277 -0
- fusesell_local/pipeline.py +978 -0
- fusesell_local/stages/__init__.py +19 -0
- fusesell_local/stages/base_stage.py +603 -0
- fusesell_local/stages/data_acquisition.py +1820 -0
- fusesell_local/stages/data_preparation.py +1238 -0
- fusesell_local/stages/follow_up.py +1728 -0
- fusesell_local/stages/initial_outreach.py +2972 -0
- fusesell_local/stages/lead_scoring.py +1452 -0
- fusesell_local/utils/__init__.py +36 -0
- fusesell_local/utils/agent_context.py +552 -0
- fusesell_local/utils/auto_setup.py +361 -0
- fusesell_local/utils/birthday_email_manager.py +467 -0
- fusesell_local/utils/data_manager.py +4857 -0
- fusesell_local/utils/event_scheduler.py +959 -0
- fusesell_local/utils/llm_client.py +342 -0
- fusesell_local/utils/logger.py +203 -0
- fusesell_local/utils/output_helpers.py +2443 -0
- fusesell_local/utils/timezone_detector.py +914 -0
- fusesell_local/utils/validators.py +436 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration Manager for FuseSell Local
|
|
3
|
+
Handles team-specific settings, prompts, and business rules
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
from typing import Dict, Any, Optional
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
import logging
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ConfigManager:
|
|
13
|
+
"""
|
|
14
|
+
Manages configuration settings for FuseSell Local.
|
|
15
|
+
Handles team-specific prompts, scoring criteria, and business rules.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, data_dir: str = "./fusesell_data"):
|
|
19
|
+
"""
|
|
20
|
+
Initialize configuration manager.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
data_dir: Directory containing configuration files
|
|
24
|
+
"""
|
|
25
|
+
self.data_dir = Path(data_dir)
|
|
26
|
+
self.config_dir = self.data_dir / "config"
|
|
27
|
+
self.logger = logging.getLogger("fusesell.config")
|
|
28
|
+
|
|
29
|
+
# Ensure config directory exists
|
|
30
|
+
self.config_dir.mkdir(parents=True, exist_ok=True)
|
|
31
|
+
|
|
32
|
+
# Cache for loaded configurations
|
|
33
|
+
self._cache = {}
|
|
34
|
+
|
|
35
|
+
def get_prompts(self, team_id: Optional[str] = None, language: str = "english") -> Dict[str, Any]:
|
|
36
|
+
"""
|
|
37
|
+
Get LLM prompts for stages, with team and language customization.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
team_id: Optional team ID for team-specific prompts
|
|
41
|
+
language: Language for prompts
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
Dictionary of prompts organized by stage
|
|
45
|
+
"""
|
|
46
|
+
cache_key = f"prompts_{team_id}_{language}"
|
|
47
|
+
|
|
48
|
+
if cache_key in self._cache:
|
|
49
|
+
return self._cache[cache_key]
|
|
50
|
+
|
|
51
|
+
# Load base prompts
|
|
52
|
+
base_prompts = self._load_json_config("prompts.json", {})
|
|
53
|
+
|
|
54
|
+
# Apply team customizations if available
|
|
55
|
+
if team_id:
|
|
56
|
+
team_prompts = self._load_team_prompts(team_id, language)
|
|
57
|
+
base_prompts = self._merge_configs(base_prompts, team_prompts)
|
|
58
|
+
|
|
59
|
+
# Apply language customizations
|
|
60
|
+
if language != "english":
|
|
61
|
+
lang_prompts = self._load_language_prompts(language)
|
|
62
|
+
base_prompts = self._merge_configs(base_prompts, lang_prompts)
|
|
63
|
+
|
|
64
|
+
self._cache[cache_key] = base_prompts
|
|
65
|
+
return base_prompts
|
|
66
|
+
|
|
67
|
+
def get_scoring_criteria(self, team_id: Optional[str] = None) -> Dict[str, Any]:
|
|
68
|
+
"""
|
|
69
|
+
Get lead scoring criteria with team customization.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
team_id: Optional team ID for team-specific criteria
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Dictionary of scoring criteria and weights
|
|
76
|
+
"""
|
|
77
|
+
cache_key = f"scoring_{team_id}"
|
|
78
|
+
|
|
79
|
+
if cache_key in self._cache:
|
|
80
|
+
return self._cache[cache_key]
|
|
81
|
+
|
|
82
|
+
# Load base scoring criteria
|
|
83
|
+
base_criteria = self._load_json_config("scoring_criteria.json", {})
|
|
84
|
+
|
|
85
|
+
# Apply team customizations if available
|
|
86
|
+
if team_id:
|
|
87
|
+
team_criteria = self._load_team_scoring(team_id)
|
|
88
|
+
base_criteria = self._merge_configs(base_criteria, team_criteria)
|
|
89
|
+
|
|
90
|
+
self._cache[cache_key] = base_criteria
|
|
91
|
+
return base_criteria
|
|
92
|
+
|
|
93
|
+
def get_email_templates(self, team_id: Optional[str] = None, language: str = "english") -> Dict[str, Any]:
|
|
94
|
+
"""
|
|
95
|
+
Get email templates with team and language customization.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
team_id: Optional team ID for team-specific templates
|
|
99
|
+
language: Language for templates
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
Dictionary of email templates
|
|
103
|
+
"""
|
|
104
|
+
cache_key = f"templates_{team_id}_{language}"
|
|
105
|
+
|
|
106
|
+
if cache_key in self._cache:
|
|
107
|
+
return self._cache[cache_key]
|
|
108
|
+
|
|
109
|
+
# Load base templates
|
|
110
|
+
base_templates = self._load_json_config("email_templates.json", {})
|
|
111
|
+
|
|
112
|
+
# Apply team customizations if available
|
|
113
|
+
if team_id:
|
|
114
|
+
team_templates = self._load_team_templates(team_id, language)
|
|
115
|
+
base_templates = self._merge_configs(base_templates, team_templates)
|
|
116
|
+
|
|
117
|
+
# Apply language customizations
|
|
118
|
+
if language != "english":
|
|
119
|
+
lang_templates = self._load_language_templates(language)
|
|
120
|
+
base_templates = self._merge_configs(base_templates, lang_templates)
|
|
121
|
+
|
|
122
|
+
self._cache[cache_key] = base_templates
|
|
123
|
+
return base_templates
|
|
124
|
+
|
|
125
|
+
def get_business_rules(self, team_id: Optional[str] = None) -> Dict[str, Any]:
|
|
126
|
+
"""
|
|
127
|
+
Get business rules and pipeline behavior settings.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
team_id: Optional team ID for team-specific rules
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
Dictionary of business rules
|
|
134
|
+
"""
|
|
135
|
+
cache_key = f"rules_{team_id}"
|
|
136
|
+
|
|
137
|
+
if cache_key in self._cache:
|
|
138
|
+
return self._cache[cache_key]
|
|
139
|
+
|
|
140
|
+
# Default business rules based on original system
|
|
141
|
+
default_rules = {
|
|
142
|
+
"pipeline": {
|
|
143
|
+
"stop_on_website_fail": True,
|
|
144
|
+
"wait_after_draft_generation": True,
|
|
145
|
+
"max_operations": 10,
|
|
146
|
+
"max_simultaneous_operations": 1,
|
|
147
|
+
"sequential_execution_only": True
|
|
148
|
+
},
|
|
149
|
+
"data_acquisition": {
|
|
150
|
+
"min_execution_time_seconds": 100,
|
|
151
|
+
"required_sources": ["website"],
|
|
152
|
+
"optional_sources": ["business_card", "linkedin", "facebook"]
|
|
153
|
+
},
|
|
154
|
+
"initial_outreach": {
|
|
155
|
+
"actions": ["draft_write", "draft_rewrite", "send", "close"],
|
|
156
|
+
"default_action": "draft_write",
|
|
157
|
+
"require_human_approval": True,
|
|
158
|
+
"one_action_per_trigger": True
|
|
159
|
+
},
|
|
160
|
+
"follow_up": {
|
|
161
|
+
"actions": ["draft_write", "draft_rewrite", "send"],
|
|
162
|
+
"require_explicit_trigger": True,
|
|
163
|
+
"analyze_previous_interactions": True
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
# Apply team customizations if available
|
|
168
|
+
if team_id:
|
|
169
|
+
team_rules = self._load_team_rules(team_id)
|
|
170
|
+
default_rules = self._merge_configs(default_rules, team_rules)
|
|
171
|
+
|
|
172
|
+
self._cache[cache_key] = default_rules
|
|
173
|
+
return default_rules
|
|
174
|
+
|
|
175
|
+
def _load_json_config(self, filename: str, default: Dict[str, Any]) -> Dict[str, Any]:
|
|
176
|
+
"""Load JSON configuration file with fallback to default."""
|
|
177
|
+
try:
|
|
178
|
+
config_file = self.config_dir / filename
|
|
179
|
+
if config_file.exists():
|
|
180
|
+
with open(config_file, 'r', encoding='utf-8') as f:
|
|
181
|
+
return json.load(f)
|
|
182
|
+
except Exception as e:
|
|
183
|
+
self.logger.warning(f"Failed to load {filename}: {str(e)}")
|
|
184
|
+
|
|
185
|
+
return default.copy()
|
|
186
|
+
|
|
187
|
+
def _load_team_prompts(self, team_id: str, language: str) -> Dict[str, Any]:
|
|
188
|
+
"""Load team-specific prompts."""
|
|
189
|
+
team_file = self.config_dir / f"team_{team_id}_prompts_{language}.json"
|
|
190
|
+
return self._load_json_config(team_file.name, {})
|
|
191
|
+
|
|
192
|
+
def _load_team_scoring(self, team_id: str) -> Dict[str, Any]:
|
|
193
|
+
"""Load team-specific scoring criteria."""
|
|
194
|
+
team_file = self.config_dir / f"team_{team_id}_scoring.json"
|
|
195
|
+
return self._load_json_config(team_file.name, {})
|
|
196
|
+
|
|
197
|
+
def _load_team_templates(self, team_id: str, language: str) -> Dict[str, Any]:
|
|
198
|
+
"""Load team-specific email templates."""
|
|
199
|
+
team_file = self.config_dir / f"team_{team_id}_templates_{language}.json"
|
|
200
|
+
return self._load_json_config(team_file.name, {})
|
|
201
|
+
|
|
202
|
+
def _load_team_rules(self, team_id: str) -> Dict[str, Any]:
|
|
203
|
+
"""Load team-specific business rules."""
|
|
204
|
+
team_file = self.config_dir / f"team_{team_id}_rules.json"
|
|
205
|
+
return self._load_json_config(team_file.name, {})
|
|
206
|
+
|
|
207
|
+
def _load_language_prompts(self, language: str) -> Dict[str, Any]:
|
|
208
|
+
"""Load language-specific prompts."""
|
|
209
|
+
lang_file = self.config_dir / f"prompts_{language}.json"
|
|
210
|
+
return self._load_json_config(lang_file.name, {})
|
|
211
|
+
|
|
212
|
+
def _load_language_templates(self, language: str) -> Dict[str, Any]:
|
|
213
|
+
"""Load language-specific email templates."""
|
|
214
|
+
lang_file = self.config_dir / f"templates_{language}.json"
|
|
215
|
+
return self._load_json_config(lang_file.name, {})
|
|
216
|
+
|
|
217
|
+
def _merge_configs(self, base: Dict[str, Any], override: Dict[str, Any]) -> Dict[str, Any]:
|
|
218
|
+
"""
|
|
219
|
+
Recursively merge configuration dictionaries.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
base: Base configuration
|
|
223
|
+
override: Override configuration
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
Merged configuration
|
|
227
|
+
"""
|
|
228
|
+
result = base.copy()
|
|
229
|
+
|
|
230
|
+
for key, value in override.items():
|
|
231
|
+
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
|
|
232
|
+
result[key] = self._merge_configs(result[key], value)
|
|
233
|
+
else:
|
|
234
|
+
result[key] = value
|
|
235
|
+
|
|
236
|
+
return result
|
|
237
|
+
|
|
238
|
+
def save_team_config(self, team_id: str, config_type: str, config_data: Dict[str, Any], language: str = "english") -> None:
|
|
239
|
+
"""
|
|
240
|
+
Save team-specific configuration.
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
team_id: Team identifier
|
|
244
|
+
config_type: Type of config (prompts, scoring, templates, rules)
|
|
245
|
+
config_data: Configuration data to save
|
|
246
|
+
language: Language for prompts/templates
|
|
247
|
+
"""
|
|
248
|
+
try:
|
|
249
|
+
if config_type in ["prompts", "templates"]:
|
|
250
|
+
filename = f"team_{team_id}_{config_type}_{language}.json"
|
|
251
|
+
else:
|
|
252
|
+
filename = f"team_{team_id}_{config_type}.json"
|
|
253
|
+
|
|
254
|
+
config_file = self.config_dir / filename
|
|
255
|
+
|
|
256
|
+
with open(config_file, 'w', encoding='utf-8') as f:
|
|
257
|
+
json.dump(config_data, f, indent=2, ensure_ascii=False)
|
|
258
|
+
|
|
259
|
+
# Clear cache for this team
|
|
260
|
+
self._clear_team_cache(team_id)
|
|
261
|
+
|
|
262
|
+
self.logger.info(f"Saved team configuration: {filename}")
|
|
263
|
+
|
|
264
|
+
except Exception as e:
|
|
265
|
+
self.logger.error(f"Failed to save team config {config_type} for {team_id}: {str(e)}")
|
|
266
|
+
raise
|
|
267
|
+
|
|
268
|
+
def _clear_team_cache(self, team_id: str) -> None:
|
|
269
|
+
"""Clear cached configurations for a specific team."""
|
|
270
|
+
keys_to_remove = [key for key in self._cache.keys() if team_id in key]
|
|
271
|
+
for key in keys_to_remove:
|
|
272
|
+
del self._cache[key]
|
|
273
|
+
|
|
274
|
+
def clear_cache(self) -> None:
|
|
275
|
+
"""Clear all cached configurations."""
|
|
276
|
+
self._cache.clear()
|
|
277
|
+
self.logger.debug("Configuration cache cleared")
|