dhisana 0.0.1.dev302__tar.gz → 0.0.1.dev304__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.
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/PKG-INFO +1 -1
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/setup.py +1 -1
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/enrich_lead_information.py +51 -3
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/generate_custom_message.py +2 -1
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/generate_email.py +14 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/generate_email_response.py +2 -1
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/generate_linkedin_response_message.py +2 -1
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/serpapi_search_tools.py +10 -40
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana.egg-info/PKG-INFO +1 -1
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/README.md +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/pyproject.toml +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/setup.cfg +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/__init__.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/cli/__init__.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/cli/cli.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/cli/datasets.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/cli/models.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/cli/predictions.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/schemas/__init__.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/schemas/common.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/schemas/sales.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/ui/__init__.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/ui/components.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/__init__.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/add_mapping.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/agent_tools.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/apollo_tools.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/assistant_tool_tag.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/built_with_api_tools.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/cache_output_tools.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/cache_output_tools_local.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/check_email_validity_tools.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/check_for_intent_signal.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/check_linkedin_url_validity.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/clay_tools.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/clean_properties.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/company_utils.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/compose_salesnav_query.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/compose_search_query.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/compose_three_step_workflow.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/composite_tools.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/dataframe_tools.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/domain_parser.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/email_body_utils.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/email_parse_helpers.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/email_provider.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/extract_email_content_for_llm.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/fetch_openai_config.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/field_validators.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/g2_tools.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/generate_content.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/generate_flow.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/generate_leads_salesnav.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/generate_linkedin_connect_message.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/generate_structured_output_internal.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/google_custom_search.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/google_oauth_tools.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/google_workspace_tools.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/hubspot_clearbit.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/hubspot_crm_tools.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/instantly_tools.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/linkedin_crawler.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/lusha_tools.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/mailgun_tools.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/mailreach_tools.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/microsoft365_tools.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/openai_assistant_and_file_utils.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/openai_helpers.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/openapi_spec_to_tools.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/openapi_tool/__init__.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/openapi_tool/api_models.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/openapi_tool/convert_openai_spec_to_tool.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/openapi_tool/openapi_tool.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/parse_linkedin_messages_txt.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/profile.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/proxy_curl_tools.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/proxycurl_search_leads.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/python_function_to_tools.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/research_lead.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/sales_navigator_crawler.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/salesforce_crm_tools.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/search_router.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/search_router_jobs.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/sendgrid_tools.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/serarch_router_local_business.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/serpapi_additional_tools.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/serpapi_google_jobs.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/serpapi_google_search.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/serpapi_local_business_search.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/serperdev_google_jobs.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/serperdev_local_business.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/serperdev_search.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/smtp_email_tools.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/test_connect.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/trasform_json.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/web_download_parse_tools.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/workflow_code_model.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/zoominfo_tools.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/workflow/__init__.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/workflow/agent.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/workflow/flow.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/workflow/task.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/workflow/test.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana.egg-info/SOURCES.txt +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana.egg-info/dependency_links.txt +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana.egg-info/entry_points.txt +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana.egg-info/requires.txt +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana.egg-info/top_level.txt +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/tests/test_agent_tools.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/tests/test_apollo_company_search.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/tests/test_apollo_lead_search.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/tests/test_connectivity.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/tests/test_email_body_utils.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/tests/test_generate_email.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/tests/test_google_document.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/tests/test_hubspot_call_logs.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/tests/test_linkedin_serper.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/tests/test_mailreach.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/tests/test_mcp_connectivity.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/tests/test_normalize_graph_datetime.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/tests/test_proxycurl_get_company_search_id.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/tests/test_proxycurl_job_count.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/tests/test_reply_thread_fallback.py +0 -0
- {dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/tests/test_structured_output_with_mcp.py +0 -0
|
@@ -658,9 +658,57 @@ async def enrich_user_info(
|
|
|
658
658
|
tool_config=tool_config,
|
|
659
659
|
)
|
|
660
660
|
if found_linkedin_url:
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
661
|
+
should_use_url = True
|
|
662
|
+
if use_strict_check and org_domain:
|
|
663
|
+
try:
|
|
664
|
+
temp_props = {"user_linkedin_url": found_linkedin_url}
|
|
665
|
+
enriched = await enrich_with_provider(temp_props, tool_config)
|
|
666
|
+
enriched_domain = (enriched.get("primary_domain_of_organization") or "").strip().lower()
|
|
667
|
+
# If no domain returned but we have an organization LinkedIn URL,
|
|
668
|
+
# look up the org to get its domain
|
|
669
|
+
if not enriched_domain:
|
|
670
|
+
enriched_org_linkedin = (enriched.get("organization_linkedin_url") or "").strip()
|
|
671
|
+
if enriched_org_linkedin:
|
|
672
|
+
try:
|
|
673
|
+
org_info = await search_organization_by_linkedin_or_domain(
|
|
674
|
+
linkedin_url=enriched_org_linkedin,
|
|
675
|
+
tool_config=tool_config,
|
|
676
|
+
)
|
|
677
|
+
enriched_domain = (org_info.get("domain") or "").strip().lower()
|
|
678
|
+
except Exception:
|
|
679
|
+
logger.debug("Could not look up org domain from LinkedIn URL: %s", enriched_org_linkedin)
|
|
680
|
+
input_org_domain = org_domain.strip().lower()
|
|
681
|
+
if input_org_domain and enriched_domain and enriched_domain != input_org_domain:
|
|
682
|
+
logger.info(
|
|
683
|
+
"Skipping Google-found LinkedIn URL %s: enriched domain '%s' does not match input domain '%s'",
|
|
684
|
+
found_linkedin_url, enriched_domain, input_org_domain,
|
|
685
|
+
)
|
|
686
|
+
should_use_url = False
|
|
687
|
+
elif input_org_domain and not enriched_domain:
|
|
688
|
+
logger.info(
|
|
689
|
+
"Skipping Google-found LinkedIn URL %s: could not determine enriched org domain to verify against input domain '%s'",
|
|
690
|
+
found_linkedin_url, input_org_domain,
|
|
691
|
+
)
|
|
692
|
+
should_use_url = False
|
|
693
|
+
except Exception:
|
|
694
|
+
logger.debug("Could not verify LinkedIn URL domain via enrichment; proceeding with found URL.")
|
|
695
|
+
if should_use_url:
|
|
696
|
+
user_linkedin_url = found_linkedin_url
|
|
697
|
+
input_properties["user_linkedin_url"] = user_linkedin_url
|
|
698
|
+
# Skip email-based LinkedIn search if strict check is on and the email
|
|
699
|
+
# domain doesn't match the expected org domain (would waste API calls
|
|
700
|
+
# searching for a person at a different company).
|
|
701
|
+
skip_email_search = False
|
|
702
|
+
if use_strict_check and org_domain and email:
|
|
703
|
+
email_domain = email.split("@")[-1].strip().lower()
|
|
704
|
+
if email_domain and org_domain.strip().lower() != email_domain:
|
|
705
|
+
skip_email_search = True
|
|
706
|
+
logger.info(
|
|
707
|
+
"Skipping email-based LinkedIn search: email domain '%s' does not match org domain '%s'",
|
|
708
|
+
email_domain, org_domain,
|
|
709
|
+
)
|
|
710
|
+
|
|
711
|
+
if not user_linkedin_url and email and not skip_email_search:
|
|
664
712
|
# If we have an email but no LinkedIn URL yet, try searching by email via Google
|
|
665
713
|
email_lookup_result = await find_user_linkedin_url_by_email_google(
|
|
666
714
|
email=email,
|
|
@@ -11,6 +11,7 @@ from dhisana.utils.generate_structured_output_internal import (
|
|
|
11
11
|
)
|
|
12
12
|
from datetime import datetime
|
|
13
13
|
from pydantic import BaseModel, ConfigDict
|
|
14
|
+
from dhisana.utils.generate_email import _sanitize_lead_for_prompt
|
|
14
15
|
|
|
15
16
|
# -----------------------------------------------------------------------------
|
|
16
17
|
# Custom Message schema
|
|
@@ -140,7 +141,7 @@ async def generate_custom_message_copy(
|
|
|
140
141
|
Below is the context in 6 main sections. Use it to craft a concise, professional message:
|
|
141
142
|
|
|
142
143
|
1) Lead Information:
|
|
143
|
-
{lead_data
|
|
144
|
+
{_sanitize_lead_for_prompt(lead_data)}
|
|
144
145
|
|
|
145
146
|
2) Sender Information:
|
|
146
147
|
Full Name: {sender_data.sender_full_name or ''}
|
|
@@ -33,6 +33,14 @@ class EmailCopy(BaseModel):
|
|
|
33
33
|
# Phone-number pattern used to redact lead phone numbers from the prompt
|
|
34
34
|
_PHONE_RE = re.compile(r"(\+?\d[\d\-.\s()]{7,}\d)")
|
|
35
35
|
|
|
36
|
+
# Large blobs that should never be forwarded into the email-generation prompt
|
|
37
|
+
_FULL_EXPORT_EXCLUDED_ADDITIONAL = {
|
|
38
|
+
"pc_person_data",
|
|
39
|
+
"pc_company_data",
|
|
40
|
+
"apollo_person_data",
|
|
41
|
+
"apollo_company_data",
|
|
42
|
+
}
|
|
43
|
+
|
|
36
44
|
|
|
37
45
|
def _sanitize_lead_for_prompt(lead: "Lead") -> str:
|
|
38
46
|
"""Return a clean JSON string of lead data suitable for an LLM prompt.
|
|
@@ -53,6 +61,12 @@ def _sanitize_lead_for_prompt(lead: "Lead") -> str:
|
|
|
53
61
|
if data.get("research_summary"):
|
|
54
62
|
data["research_summary"] = _PHONE_RE.sub("[redacted]", data["research_summary"])
|
|
55
63
|
|
|
64
|
+
# Strip bulky raw-export blobs from additional_properties
|
|
65
|
+
ap = data.get("additional_properties")
|
|
66
|
+
if isinstance(ap, dict):
|
|
67
|
+
for key in _FULL_EXPORT_EXCLUDED_ADDITIONAL:
|
|
68
|
+
ap.pop(key, None)
|
|
69
|
+
|
|
56
70
|
return json.dumps(data, indent=2, default=str)
|
|
57
71
|
|
|
58
72
|
|
|
@@ -11,6 +11,7 @@ from dhisana.schemas.sales import (
|
|
|
11
11
|
SenderInfo
|
|
12
12
|
)
|
|
13
13
|
from dhisana.utils.assistant_tool_tag import assistant_tool
|
|
14
|
+
from dhisana.utils.generate_email import _sanitize_lead_for_prompt
|
|
14
15
|
from dhisana.utils.generate_structured_output_internal import (
|
|
15
16
|
get_structured_output_with_assistant_and_vector_store,
|
|
16
17
|
get_structured_output_internal
|
|
@@ -283,7 +284,7 @@ async def generate_inbound_email_response_copy(
|
|
|
283
284
|
if cleaned_context.current_conversation_context.current_email_thread else []}
|
|
284
285
|
|
|
285
286
|
2) Lead information:
|
|
286
|
-
{lead_data
|
|
287
|
+
{_sanitize_lead_for_prompt(lead_data)}
|
|
287
288
|
|
|
288
289
|
Sender information:
|
|
289
290
|
- Full name: {sender_data.sender_full_name or ''}
|
|
@@ -14,6 +14,7 @@ from dhisana.utils.generate_structured_output_internal import (
|
|
|
14
14
|
get_structured_output_with_assistant_and_vector_store
|
|
15
15
|
)
|
|
16
16
|
from dhisana.utils.assistant_tool_tag import assistant_tool
|
|
17
|
+
from dhisana.utils.generate_email import _sanitize_lead_for_prompt
|
|
17
18
|
import datetime
|
|
18
19
|
|
|
19
20
|
# ---------------------------------------------------------------------------------------
|
|
@@ -112,7 +113,7 @@ async def generate_linkedin_response_message_copy(
|
|
|
112
113
|
|
|
113
114
|
2.
|
|
114
115
|
Lead Information:
|
|
115
|
-
{lead_data
|
|
116
|
+
{_sanitize_lead_for_prompt(lead_data)}
|
|
116
117
|
|
|
117
118
|
Sender Information:
|
|
118
119
|
Full Name: {sender_data.sender_full_name or ''}
|
|
@@ -401,12 +401,6 @@ async def find_user_linkedin_url_by_email_google(
|
|
|
401
401
|
if query and query not in queries:
|
|
402
402
|
queries.append(query)
|
|
403
403
|
|
|
404
|
-
def add_query_parts(*parts: str) -> None:
|
|
405
|
-
tokens = [part.strip() for part in parts if part and part.strip()]
|
|
406
|
-
if not tokens:
|
|
407
|
-
return
|
|
408
|
-
add_query(" ".join(tokens))
|
|
409
|
-
|
|
410
404
|
enriched_terms = []
|
|
411
405
|
if user_name:
|
|
412
406
|
enriched_terms.append(f'"{user_name}"')
|
|
@@ -418,47 +412,23 @@ async def find_user_linkedin_url_by_email_google(
|
|
|
418
412
|
enriched_terms.append(f'"{user_location}"')
|
|
419
413
|
base_hint = " ".join(enriched_terms)
|
|
420
414
|
|
|
421
|
-
#
|
|
422
|
-
|
|
423
|
-
add_query_parts(normalized_email, "linkedin.com", base_hint)
|
|
424
|
-
add_query_parts(normalized_email, "linkedin", base_hint)
|
|
425
|
-
add_query_parts(normalized_email, base_hint)
|
|
426
|
-
add_query(f'"{normalized_email}" "linkedin.com/in" {base_hint}')
|
|
427
|
-
add_query(f'"{normalized_email}" "linkedin.com" {base_hint}')
|
|
428
|
-
add_query(f'"{normalized_email}" linkedin {base_hint}')
|
|
415
|
+
# 1) Best query: site-scoped with exact email
|
|
416
|
+
add_query(f'site:linkedin.com/in "{normalized_email}" {base_hint}')
|
|
429
417
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
add_query_parts(email_local_part, "linkedin.com", base_hint)
|
|
433
|
-
add_query_parts(email_local_part, "linkedin", base_hint)
|
|
434
|
-
add_query(f'"{email_local_part}" "linkedin.com/in" {base_hint}')
|
|
435
|
-
add_query(f'"{email_local_part}" "linkedin.com" {base_hint}')
|
|
418
|
+
# 2) Exact email with linkedin scope (Google may surface LinkedIn even without site:)
|
|
419
|
+
add_query(f'"{normalized_email}" linkedin.com/in {base_hint}')
|
|
436
420
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
add_query(f'"{email_local_humanized}" linkedin {base_hint}')
|
|
421
|
+
# 3) Unquoted email with linkedin scope (catches partial matches)
|
|
422
|
+
add_query(f'{normalized_email} linkedin.com/in {base_hint}')
|
|
440
423
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
if email_local_part:
|
|
424
|
+
# 4) Local part fallback with site scope (e.g. "santosh" on linkedin)
|
|
425
|
+
if email_local_part and email_local_part != normalized_email:
|
|
445
426
|
add_query(f'site:linkedin.com/in "{email_local_part}" {base_hint}')
|
|
446
427
|
|
|
447
|
-
|
|
428
|
+
# 5) Humanized local part fallback (e.g. "john smith" from "john.smith")
|
|
429
|
+
if email_local_humanized and email_local_humanized not in {email_local_part, normalized_email}:
|
|
448
430
|
add_query(f'site:linkedin.com/in "{email_local_humanized}" {base_hint}')
|
|
449
431
|
|
|
450
|
-
if base_hint:
|
|
451
|
-
lookup_hint = user_name or email_local_humanized or email_local_part or normalized_email
|
|
452
|
-
add_query(
|
|
453
|
-
f'site:linkedin.com/in "{normalized_email}" {base_hint} '
|
|
454
|
-
f'intitle:"{lookup_hint}" -intitle:"profiles"'
|
|
455
|
-
)
|
|
456
|
-
if email_local_humanized:
|
|
457
|
-
add_query(
|
|
458
|
-
f'site:linkedin.com/in "{email_local_humanized}" {base_hint} '
|
|
459
|
-
f'intitle:"{lookup_hint}" -intitle:"profiles"'
|
|
460
|
-
)
|
|
461
|
-
|
|
462
432
|
candidate_records: List[Dict[str, str]] = []
|
|
463
433
|
seen_links: Set[str] = set()
|
|
464
434
|
best_llm_choice: Optional[LinkedinCandidateChoice] = None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/check_email_validity_tools.py
RENAMED
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/check_linkedin_url_validity.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/compose_three_step_workflow.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/extract_email_content_for_llm.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/generate_linkedin_connect_message.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/openai_assistant_and_file_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/openapi_tool/openapi_tool.py
RENAMED
|
File without changes
|
{dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/parse_linkedin_messages_txt.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/serarch_router_local_business.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev302 → dhisana-0.0.1.dev304}/src/dhisana/utils/serpapi_local_business_search.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|