fusesell 1.3.2__tar.gz → 1.3.3__tar.gz

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.

Potentially problematic release.


This version of fusesell might be problematic. Click here for more details.

Files changed (43) hide show
  1. {fusesell-1.3.2 → fusesell-1.3.3}/CHANGELOG.md +15 -5
  2. {fusesell-1.3.2/fusesell.egg-info → fusesell-1.3.3}/PKG-INFO +1 -1
  3. {fusesell-1.3.2 → fusesell-1.3.3/fusesell.egg-info}/PKG-INFO +1 -1
  4. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/__init__.py +1 -1
  5. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/api.py +3 -1
  6. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/tests/test_api.py +31 -14
  7. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/utils/__init__.py +12 -11
  8. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/utils/llm_client.py +78 -19
  9. {fusesell-1.3.2 → fusesell-1.3.3}/pyproject.toml +1 -1
  10. {fusesell-1.3.2 → fusesell-1.3.3}/LICENSE +0 -0
  11. {fusesell-1.3.2 → fusesell-1.3.3}/MANIFEST.in +0 -0
  12. {fusesell-1.3.2 → fusesell-1.3.3}/README.md +0 -0
  13. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell.egg-info/SOURCES.txt +0 -0
  14. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell.egg-info/dependency_links.txt +0 -0
  15. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell.egg-info/entry_points.txt +0 -0
  16. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell.egg-info/requires.txt +0 -0
  17. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell.egg-info/top_level.txt +0 -0
  18. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell.py +0 -0
  19. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/cli.py +0 -0
  20. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/config/__init__.py +0 -0
  21. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/config/prompts.py +0 -0
  22. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/config/settings.py +0 -0
  23. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/pipeline.py +0 -0
  24. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/stages/__init__.py +0 -0
  25. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/stages/base_stage.py +0 -0
  26. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/stages/data_acquisition.py +0 -0
  27. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/stages/data_preparation.py +0 -0
  28. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/stages/follow_up.py +0 -0
  29. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/stages/initial_outreach.py +0 -0
  30. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/stages/lead_scoring.py +0 -0
  31. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/tests/conftest.py +0 -0
  32. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/tests/test_cli.py +0 -0
  33. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/tests/test_data_manager_products.py +0 -0
  34. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/tests/test_data_manager_sales_process.py +0 -0
  35. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/tests/test_data_manager_teams.py +0 -0
  36. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/utils/birthday_email_manager.py +0 -0
  37. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/utils/data_manager.py +0 -0
  38. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/utils/event_scheduler.py +0 -0
  39. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/utils/logger.py +0 -0
  40. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/utils/timezone_detector.py +0 -0
  41. {fusesell-1.3.2 → fusesell-1.3.3}/fusesell_local/utils/validators.py +0 -0
  42. {fusesell-1.3.2 → fusesell-1.3.3}/requirements.txt +0 -0
  43. {fusesell-1.3.2 → fusesell-1.3.3}/setup.cfg +0 -0
@@ -1,10 +1,20 @@
1
1
  # Changelog
2
2
 
3
- All notable changes to FuseSell Local will be documented in this file.
4
-
5
- # [1.3.2] - 2025-10-24
6
-
7
- ### Changed
3
+ All notable changes to FuseSell Local will be documented in this file.
4
+
5
+ # [1.3.3] - 2025-10-25
6
+
7
+ ### Changed
8
+ - Added a shared `normalize_llm_base_url` helper so every entry point (CLI, programmatic API, birthday scheduler, etc.) consistently appends `/v1` to OpenAI-style endpoints while leaving Azure deployment URLs untouched.
9
+ - Exported the helper for downstream consumers, enabling RealTimeX flows to import the canonical logic instead of maintaining their own copy.
10
+
11
+ ### Fixed
12
+ - Resolved repeated `APIConnectionError` retries when custom LLM base URLs were missing `/v1`, restoring successful chat completion calls across all pipeline stages.
13
+ - Added regression coverage to ensure Azure endpoints remain unchanged while standard endpoints are normalized, guarding against future regressions.
14
+
15
+ # [1.3.2] - 2025-10-24
16
+
17
+ ### Changed
8
18
  - Removed the deterministic fallback template so every initial-outreach draft now originates from the LLM path; when the model call fails the stage reports an error instead of emitting canned copy.
9
19
 
10
20
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fusesell
3
- Version: 1.3.2
3
+ Version: 1.3.3
4
4
  Summary: Local implementation of FuseSell AI sales automation pipeline
5
5
  Author-email: FuseSell Team <team@fusesell.ai>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fusesell
3
- Version: 1.3.2
3
+ Version: 1.3.3
4
4
  Summary: Local implementation of FuseSell AI sales automation pipeline
5
5
  Author-email: FuseSell Team <team@fusesell.ai>
6
6
  License-Expression: MIT
@@ -32,6 +32,6 @@ __all__ = [
32
32
  "validate_config",
33
33
  ]
34
34
 
35
- __version__ = "1.3.2"
35
+ __version__ = "1.3.3"
36
36
  __author__ = "FuseSell Team"
37
37
  __description__ = "Local implementation of FuseSell AI sales automation pipeline"
@@ -16,6 +16,7 @@ from typing import Any, Callable, Dict, Mapping, MutableMapping, Optional, Seque
16
16
  from .pipeline import FuseSellPipeline
17
17
  from .utils.logger import setup_logging as _setup_logging
18
18
  from .utils.validators import InputValidator
19
+ from .utils.llm_client import normalize_llm_base_url
19
20
 
20
21
 
21
22
  class ConfigValidationError(ValueError):
@@ -138,6 +139,8 @@ def build_config(options: OptionsType) -> Dict[str, Any]:
138
139
  "dry_run": _coerce_bool(_get("dry_run")),
139
140
  }
140
141
 
142
+ config["llm_base_url"] = normalize_llm_base_url(config.get("llm_base_url"))
143
+
141
144
  return config
142
145
 
143
146
 
@@ -338,4 +341,3 @@ def execute_pipeline(
338
341
  }
339
342
 
340
343
  return run_pipeline(config)
341
-
@@ -24,20 +24,37 @@ def base_options(**overrides):
24
24
  return options
25
25
 
26
26
 
27
- def test_build_config_generates_defaults():
28
- config = build_config(base_options())
29
-
30
- assert config["execution_id"].startswith("fusesell_")
31
- assert config["output_format"] == "json"
32
- assert config["skip_stages"] == []
33
- assert config["send_immediately"] is False
34
-
35
-
36
- def test_validate_config_detects_missing_sources():
37
- config = build_config(
38
- base_options(
39
- input_description="",
40
- input_website="",
27
+ def test_build_config_generates_defaults():
28
+ config = build_config(base_options())
29
+
30
+ assert config["execution_id"].startswith("fusesell_")
31
+ assert config["output_format"] == "json"
32
+ assert config["skip_stages"] == []
33
+ assert config["send_immediately"] is False
34
+
35
+
36
+ def test_build_config_normalizes_llm_base_url():
37
+ config = build_config(
38
+ base_options(
39
+ llm_base_url="https://custom-llm.example.com",
40
+ )
41
+ )
42
+
43
+ assert config["llm_base_url"] == "https://custom-llm.example.com/v1"
44
+
45
+
46
+ def test_build_config_preserves_azure_base_url():
47
+ azure_url = "https://rtx-openai.openai.azure.com/openai/deployments/gpt4"
48
+ config = build_config(base_options(llm_base_url=azure_url))
49
+
50
+ assert config["llm_base_url"] == azure_url
51
+
52
+
53
+ def test_validate_config_detects_missing_sources():
54
+ config = build_config(
55
+ base_options(
56
+ input_description="",
57
+ input_website="",
41
58
  input_freetext="",
42
59
  )
43
60
  )
@@ -2,14 +2,15 @@
2
2
  FuseSell Utilities - Common utilities and helper functions
3
3
  """
4
4
 
5
- from .data_manager import LocalDataManager
6
- from .llm_client import LLMClient
7
- from .validators import InputValidator
8
- from .logger import setup_logging
9
-
10
- __all__ = [
11
- 'LocalDataManager',
12
- 'LLMClient',
13
- 'InputValidator',
14
- 'setup_logging'
15
- ]
5
+ from .data_manager import LocalDataManager
6
+ from .llm_client import LLMClient, normalize_llm_base_url
7
+ from .validators import InputValidator
8
+ from .logger import setup_logging
9
+
10
+ __all__ = [
11
+ 'LocalDataManager',
12
+ 'LLMClient',
13
+ 'normalize_llm_base_url',
14
+ 'InputValidator',
15
+ 'setup_logging'
16
+ ]
@@ -2,17 +2,74 @@
2
2
  LLM Client for OpenAI API integration
3
3
  """
4
4
 
5
- try:
6
- import openai
7
- OPENAI_AVAILABLE = True
8
- except ImportError:
9
- OPENAI_AVAILABLE = False
10
- openai = None
11
-
12
- from typing import Dict, Any, List, Optional
13
- import logging
14
- import time
15
- import json
5
+ try:
6
+ import openai
7
+ OPENAI_AVAILABLE = True
8
+ except ImportError:
9
+ OPENAI_AVAILABLE = False
10
+ openai = None
11
+
12
+ from typing import Dict, Any, List, Optional
13
+ import logging
14
+ import time
15
+ import json
16
+ from urllib.parse import urlsplit, urlunsplit
17
+
18
+
19
+ def normalize_llm_base_url(base_url: Optional[str], provider: Optional[str] = None) -> Optional[str]:
20
+ """
21
+ Ensure LLM base URLs point to the OpenAI-compatible /v1 endpoint unless they already target
22
+ Azure deployment paths that do not expect the suffix.
23
+
24
+ Args:
25
+ base_url: User-provided base URL.
26
+ provider: Optional provider hint (e.g., 'azure-openai').
27
+
28
+ Returns:
29
+ Normalized base URL with `/v1` appended when needed, or ``None`` if input is empty.
30
+ """
31
+ if not base_url:
32
+ return None
33
+
34
+ normalized = base_url.strip()
35
+ if not normalized:
36
+ return None
37
+
38
+ provider_hint = (provider or "").lower()
39
+ if provider_hint.startswith("azure") or "openai.azure.com" in normalized.lower():
40
+ return normalized.rstrip("/")
41
+
42
+ try:
43
+ parsed = urlsplit(normalized)
44
+ except ValueError:
45
+ parsed = None
46
+
47
+ if parsed and parsed.scheme and parsed.netloc:
48
+ path = parsed.path.rstrip("/")
49
+ segments = [segment for segment in path.split("/") if segment]
50
+
51
+ if not segments:
52
+ new_path = "/v1"
53
+ elif segments[-1] in {"v1", "v1beta"} or "v1" in segments or "deployments" in segments:
54
+ new_path = "/" + "/".join(segments)
55
+ else:
56
+ new_path = f"{path}/v1" if path else "/v1"
57
+
58
+ rebuilt = urlunsplit(
59
+ (
60
+ parsed.scheme,
61
+ parsed.netloc,
62
+ new_path,
63
+ parsed.query,
64
+ parsed.fragment,
65
+ )
66
+ )
67
+ return rebuilt.rstrip("/")
68
+
69
+ stripped = normalized.rstrip("/")
70
+ if stripped.endswith("/v1") or "/v1/" in stripped:
71
+ return stripped
72
+ return f"{stripped}/v1"
16
73
 
17
74
 
18
75
  class LLMClient:
@@ -35,13 +92,15 @@ class LLMClient:
35
92
 
36
93
  self.api_key = api_key
37
94
  self.model = model
38
- self.logger = logging.getLogger("fusesell.llm_client")
39
-
40
- # Initialize OpenAI client
41
- if base_url:
42
- self.client = openai.OpenAI(api_key=api_key, base_url=base_url)
43
- else:
44
- self.client = openai.OpenAI(api_key=api_key)
95
+ self.logger = logging.getLogger("fusesell.llm_client")
96
+
97
+ normalized_base_url = normalize_llm_base_url(base_url)
98
+
99
+ # Initialize OpenAI client
100
+ if normalized_base_url:
101
+ self.client = openai.OpenAI(api_key=api_key, base_url=normalized_base_url)
102
+ else:
103
+ self.client = openai.OpenAI(api_key=api_key)
45
104
 
46
105
  def chat_completion(
47
106
  self,
@@ -280,4 +339,4 @@ Response:"""
280
339
  return len(response) > 0
281
340
  except Exception as e:
282
341
  self.logger.error(f"API key validation failed: {str(e)}")
283
- return False
342
+ return False
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "fusesell"
7
- version = "1.3.2"
7
+ version = "1.3.3"
8
8
  description = "Local implementation of FuseSell AI sales automation pipeline"
9
9
  readme = "README.md"
10
10
  license = "MIT"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes