dhisana 0.0.1.dev243__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 (102) hide show
  1. dhisana/__init__.py +1 -0
  2. dhisana/cli/__init__.py +1 -0
  3. dhisana/cli/cli.py +20 -0
  4. dhisana/cli/datasets.py +27 -0
  5. dhisana/cli/models.py +26 -0
  6. dhisana/cli/predictions.py +20 -0
  7. dhisana/schemas/__init__.py +1 -0
  8. dhisana/schemas/common.py +399 -0
  9. dhisana/schemas/sales.py +965 -0
  10. dhisana/ui/__init__.py +1 -0
  11. dhisana/ui/components.py +472 -0
  12. dhisana/utils/__init__.py +1 -0
  13. dhisana/utils/add_mapping.py +352 -0
  14. dhisana/utils/agent_tools.py +51 -0
  15. dhisana/utils/apollo_tools.py +1597 -0
  16. dhisana/utils/assistant_tool_tag.py +4 -0
  17. dhisana/utils/built_with_api_tools.py +282 -0
  18. dhisana/utils/cache_output_tools.py +98 -0
  19. dhisana/utils/cache_output_tools_local.py +78 -0
  20. dhisana/utils/check_email_validity_tools.py +717 -0
  21. dhisana/utils/check_for_intent_signal.py +107 -0
  22. dhisana/utils/check_linkedin_url_validity.py +209 -0
  23. dhisana/utils/clay_tools.py +43 -0
  24. dhisana/utils/clean_properties.py +135 -0
  25. dhisana/utils/company_utils.py +60 -0
  26. dhisana/utils/compose_salesnav_query.py +259 -0
  27. dhisana/utils/compose_search_query.py +759 -0
  28. dhisana/utils/compose_three_step_workflow.py +234 -0
  29. dhisana/utils/composite_tools.py +137 -0
  30. dhisana/utils/dataframe_tools.py +237 -0
  31. dhisana/utils/domain_parser.py +45 -0
  32. dhisana/utils/email_body_utils.py +72 -0
  33. dhisana/utils/email_parse_helpers.py +132 -0
  34. dhisana/utils/email_provider.py +375 -0
  35. dhisana/utils/enrich_lead_information.py +933 -0
  36. dhisana/utils/extract_email_content_for_llm.py +101 -0
  37. dhisana/utils/fetch_openai_config.py +129 -0
  38. dhisana/utils/field_validators.py +426 -0
  39. dhisana/utils/g2_tools.py +104 -0
  40. dhisana/utils/generate_content.py +41 -0
  41. dhisana/utils/generate_custom_message.py +271 -0
  42. dhisana/utils/generate_email.py +278 -0
  43. dhisana/utils/generate_email_response.py +465 -0
  44. dhisana/utils/generate_flow.py +102 -0
  45. dhisana/utils/generate_leads_salesnav.py +303 -0
  46. dhisana/utils/generate_linkedin_connect_message.py +224 -0
  47. dhisana/utils/generate_linkedin_response_message.py +317 -0
  48. dhisana/utils/generate_structured_output_internal.py +462 -0
  49. dhisana/utils/google_custom_search.py +267 -0
  50. dhisana/utils/google_oauth_tools.py +727 -0
  51. dhisana/utils/google_workspace_tools.py +1294 -0
  52. dhisana/utils/hubspot_clearbit.py +96 -0
  53. dhisana/utils/hubspot_crm_tools.py +2440 -0
  54. dhisana/utils/instantly_tools.py +149 -0
  55. dhisana/utils/linkedin_crawler.py +168 -0
  56. dhisana/utils/lusha_tools.py +333 -0
  57. dhisana/utils/mailgun_tools.py +156 -0
  58. dhisana/utils/mailreach_tools.py +123 -0
  59. dhisana/utils/microsoft365_tools.py +455 -0
  60. dhisana/utils/openai_assistant_and_file_utils.py +267 -0
  61. dhisana/utils/openai_helpers.py +977 -0
  62. dhisana/utils/openapi_spec_to_tools.py +45 -0
  63. dhisana/utils/openapi_tool/__init__.py +1 -0
  64. dhisana/utils/openapi_tool/api_models.py +633 -0
  65. dhisana/utils/openapi_tool/convert_openai_spec_to_tool.py +271 -0
  66. dhisana/utils/openapi_tool/openapi_tool.py +319 -0
  67. dhisana/utils/parse_linkedin_messages_txt.py +100 -0
  68. dhisana/utils/profile.py +37 -0
  69. dhisana/utils/proxy_curl_tools.py +1226 -0
  70. dhisana/utils/proxycurl_search_leads.py +426 -0
  71. dhisana/utils/python_function_to_tools.py +83 -0
  72. dhisana/utils/research_lead.py +176 -0
  73. dhisana/utils/sales_navigator_crawler.py +1103 -0
  74. dhisana/utils/salesforce_crm_tools.py +477 -0
  75. dhisana/utils/search_router.py +131 -0
  76. dhisana/utils/search_router_jobs.py +51 -0
  77. dhisana/utils/sendgrid_tools.py +162 -0
  78. dhisana/utils/serarch_router_local_business.py +75 -0
  79. dhisana/utils/serpapi_additional_tools.py +290 -0
  80. dhisana/utils/serpapi_google_jobs.py +117 -0
  81. dhisana/utils/serpapi_google_search.py +188 -0
  82. dhisana/utils/serpapi_local_business_search.py +129 -0
  83. dhisana/utils/serpapi_search_tools.py +852 -0
  84. dhisana/utils/serperdev_google_jobs.py +125 -0
  85. dhisana/utils/serperdev_local_business.py +154 -0
  86. dhisana/utils/serperdev_search.py +233 -0
  87. dhisana/utils/smtp_email_tools.py +582 -0
  88. dhisana/utils/test_connect.py +2087 -0
  89. dhisana/utils/trasform_json.py +173 -0
  90. dhisana/utils/web_download_parse_tools.py +189 -0
  91. dhisana/utils/workflow_code_model.py +5 -0
  92. dhisana/utils/zoominfo_tools.py +357 -0
  93. dhisana/workflow/__init__.py +1 -0
  94. dhisana/workflow/agent.py +18 -0
  95. dhisana/workflow/flow.py +44 -0
  96. dhisana/workflow/task.py +43 -0
  97. dhisana/workflow/test.py +90 -0
  98. dhisana-0.0.1.dev243.dist-info/METADATA +43 -0
  99. dhisana-0.0.1.dev243.dist-info/RECORD +102 -0
  100. dhisana-0.0.1.dev243.dist-info/WHEEL +5 -0
  101. dhisana-0.0.1.dev243.dist-info/entry_points.txt +2 -0
  102. dhisana-0.0.1.dev243.dist-info/top_level.txt +1 -0
@@ -0,0 +1,156 @@
1
+ import asyncio
2
+ import logging
3
+ import os
4
+ from typing import Optional, List, Dict
5
+
6
+ import aiohttp
7
+
8
+ from dhisana.utils.assistant_tool_tag import assistant_tool
9
+ from dhisana.schemas.common import SendEmailContext
10
+ from dhisana.utils.email_body_utils import body_variants
11
+
12
+
13
+ def get_mailgun_notify_key(tool_config: Optional[List[Dict]] = None) -> str:
14
+ """
15
+ Retrieve the Mailgun API key from tool_config or environment.
16
+
17
+ Looks for an integration named "mailgun" and reads configuration item
18
+ with name "apiKey". Falls back to env var MAILGUN_NOTIFY_KEY.
19
+ """
20
+ key = None
21
+ if tool_config:
22
+ cfg = next((item for item in tool_config if item.get("name") == "mailgun"), None)
23
+ if cfg:
24
+ cfg_map = {i["name"]: i["value"] for i in cfg.get("configuration", []) if i}
25
+ key = cfg_map.get("apiKey")
26
+ key = key or os.getenv("MAILGUN_NOTIFY_KEY")
27
+ if not key:
28
+ raise ValueError(
29
+ "Mailgun integration is not configured. Please configure the connection to Mailgun in Integrations."
30
+ )
31
+ return key
32
+
33
+
34
+ def get_mailgun_notify_domain(tool_config: Optional[List[Dict]] = None) -> str:
35
+ """
36
+ Retrieve the Mailgun domain from tool_config or environment.
37
+
38
+ Looks for an integration named "mailgun" and reads configuration item
39
+ with name "domain" (preferred) or legacy "notifyDomain".
40
+ Falls back to env var MAILGUN_DOMAIN, then MAILGUN_NOTIFY_DOMAIN.
41
+ """
42
+ domain = None
43
+ if tool_config:
44
+ cfg = next((item for item in tool_config if item.get("name") == "mailgun"), None)
45
+ if cfg:
46
+ cfg_map = {i["name"]: i["value"] for i in cfg.get("configuration", []) if i}
47
+ domain = cfg_map.get("domain") or cfg_map.get("notifyDomain")
48
+ domain = domain or os.getenv("MAILGUN_DOMAIN") or os.getenv("MAILGUN_NOTIFY_DOMAIN")
49
+ if not domain:
50
+ raise ValueError(
51
+ "Mailgun integration is not configured. Please configure the connection to Mailgun in Integrations."
52
+ )
53
+ return domain
54
+
55
+
56
+ @assistant_tool
57
+ async def send_email_with_mailgun(
58
+ sender: str,
59
+ recipients: List[str],
60
+ subject: str,
61
+ message: str,
62
+ tool_config: Optional[List[Dict]] = None,
63
+ body_format: Optional[str] = None,
64
+ ):
65
+ """
66
+ Send an email using the Mailgun API.
67
+
68
+ Parameters:
69
+ - sender: Email address string, e.g. "Alice <alice@example.com>" or just address.
70
+ - recipients: List of recipient email addresses.
71
+ - subject: Subject string.
72
+ - message: HTML content body.
73
+ - tool_config: Optional integrations config list.
74
+ """
75
+ try:
76
+ api_key = get_mailgun_notify_key(tool_config)
77
+ domain = get_mailgun_notify_domain(tool_config)
78
+
79
+ body = message or ""
80
+ data = {
81
+ "from": sender,
82
+ "to": recipients,
83
+ "subject": subject,
84
+ }
85
+
86
+ plain_body, html_body, _ = body_variants(body, body_format)
87
+ data["text"] = plain_body
88
+ data["html"] = html_body
89
+
90
+ async with aiohttp.ClientSession() as session:
91
+ async with session.post(
92
+ f"https://api.mailgun.net/v3/{domain}/messages",
93
+ auth=aiohttp.BasicAuth("api", api_key),
94
+ data=data,
95
+ ) as response:
96
+ # Try to return JSON payload if available
97
+ try:
98
+ return await response.json()
99
+ except Exception:
100
+ return await response.text()
101
+ except Exception as ex:
102
+ logging.warning(f"Error sending email via Mailgun: {ex}")
103
+ return {"error": str(ex)}
104
+
105
+
106
+ async def send_email_using_mailgun_async(
107
+ send_email_context: SendEmailContext,
108
+ tool_config: Optional[List[Dict]] = None,
109
+ ) -> str:
110
+ """
111
+ Provider-style wrapper for Mailgun that accepts SendEmailContext and returns an id string.
112
+ """
113
+ api_key = get_mailgun_notify_key(tool_config)
114
+ domain = get_mailgun_notify_domain(tool_config)
115
+
116
+ plain_body, html_body, _ = body_variants(
117
+ send_email_context.body,
118
+ getattr(send_email_context, "body_format", None),
119
+ )
120
+
121
+ data = {
122
+ "from": f"{send_email_context.sender_name} <{send_email_context.sender_email}>",
123
+ "to": [send_email_context.recipient],
124
+ "subject": send_email_context.subject,
125
+ "text": plain_body,
126
+ "html": html_body,
127
+ }
128
+
129
+ extra_headers = getattr(send_email_context, "headers", None) or {}
130
+ for header, value in extra_headers.items():
131
+ if not header or value is None:
132
+ continue
133
+ data[f"h:{header}"] = str(value)
134
+
135
+ async with aiohttp.ClientSession() as session:
136
+ async with session.post(
137
+ f"https://api.mailgun.net/v3/{domain}/messages",
138
+ auth=aiohttp.BasicAuth("api", api_key),
139
+ data=data,
140
+ ) as response:
141
+ # Raise if not 2xx to match other providers' behavior
142
+ if response.status < 200 or response.status >= 300:
143
+ try:
144
+ detail = await response.text()
145
+ except Exception:
146
+ detail = f"status={response.status}"
147
+ raise RuntimeError(f"Mailgun send failed: {detail}")
148
+ try:
149
+ payload = await response.json()
150
+ except Exception:
151
+ payload = {"message": await response.text()}
152
+
153
+ # Normalise return value akin to other providers
154
+ msg_id = payload.get("id") if isinstance(payload, dict) else None
155
+ await asyncio.sleep(20)
156
+ return msg_id or str(payload)
@@ -0,0 +1,123 @@
1
+ import os
2
+ import aiohttp
3
+ import logging
4
+ from typing import List, Dict, Any, Optional
5
+ from dhisana.utils.assistant_tool_tag import assistant_tool
6
+
7
+ logging.basicConfig(level=logging.INFO)
8
+ logger = logging.getLogger(__name__)
9
+
10
+ base_url = 'https://api.mailreach.co/api/v1'
11
+
12
+
13
+ def get_mailreach_api_key(tool_config: Optional[List[Dict]] = None) -> str:
14
+ """
15
+ Retrieves the MailReach API key from the provided tool configuration or environment variables.
16
+
17
+ Args:
18
+ tool_config (list): A list of dictionaries containing the tool configuration.
19
+ Each dictionary should have a "name" key and a "configuration" key,
20
+ where "configuration" is a list of dictionaries containing "name" and "value" keys.
21
+
22
+ Returns:
23
+ str: The MailReach API key.
24
+
25
+ Raises:
26
+ ValueError: If the MailReach integration has not been configured.
27
+ """
28
+ api_key = None
29
+
30
+ if tool_config:
31
+ mailreach_config = next(
32
+ (item for item in tool_config if item.get("name") == "mailreach"), None
33
+ )
34
+ if mailreach_config:
35
+ config_map = {
36
+ item["name"]: item["value"]
37
+ for item in mailreach_config.get("configuration", [])
38
+ if item
39
+ }
40
+ api_key = config_map.get("apiKey")
41
+
42
+ api_key = api_key or os.getenv("MAILREACH_API_KEY")
43
+
44
+ if not api_key:
45
+ raise ValueError(
46
+ "MailReach integration is not configured. Please configure the connection to MailReach in Integrations."
47
+ )
48
+
49
+ return api_key
50
+
51
+
52
+ def get_mailreach_headers(tool_config: Optional[List[Dict]] = None) -> Dict[str, str]:
53
+ """
54
+ Get the headers required for MailReach API requests.
55
+
56
+ Args:
57
+ tool_config (list): Optional tool configuration containing API credentials.
58
+
59
+ Returns:
60
+ Dict[str, str]: Headers dictionary with x-api-key and Content-Type.
61
+ """
62
+ api_key = get_mailreach_api_key(tool_config)
63
+ headers = {
64
+ "x-api-key": api_key,
65
+ "Content-Type": "application/json"
66
+ }
67
+ return headers
68
+
69
+
70
+ async def _handle_mailreach_response(response: aiohttp.ClientResponse) -> Any:
71
+ """
72
+ Handle MailReach API responses consistently.
73
+
74
+ Args:
75
+ response: The aiohttp ClientResponse object.
76
+
77
+ Returns:
78
+ The JSON response data.
79
+
80
+ Raises:
81
+ aiohttp.ClientResponseError: For rate limits or other errors.
82
+ """
83
+ if response.status == 200:
84
+ return await response.json()
85
+ elif response.status == 429:
86
+ raise aiohttp.ClientResponseError(
87
+ request_info=response.request_info,
88
+ history=response.history,
89
+ status=response.status,
90
+ message="Rate limit exceeded",
91
+ headers=response.headers
92
+ )
93
+ else:
94
+ error_message = await response.text()
95
+ logger.error(f"MailReach API Error {response.status}: {error_message}")
96
+ response.raise_for_status()
97
+
98
+
99
+ @assistant_tool
100
+ async def ping_mailreach(
101
+ tool_config: Optional[List[Dict]] = None
102
+ ) -> Dict[str, Any]:
103
+ """
104
+ Ping the MailReach API to verify connectivity and authentication.
105
+
106
+ This is a simple endpoint to test if your API key is valid and the service is accessible.
107
+
108
+ Args:
109
+ tool_config (list): Optional tool configuration containing API credentials.
110
+
111
+ Returns:
112
+ Dict[str, Any]: Response from the ping endpoint, typically containing a success message.
113
+ """
114
+ url = f"{base_url}/ping"
115
+ headers = get_mailreach_headers(tool_config)
116
+
117
+ logger.info("Pinging MailReach API...")
118
+
119
+ async with aiohttp.ClientSession() as session:
120
+ async with session.get(url, headers=headers) as response:
121
+ result = await _handle_mailreach_response(response)
122
+ logger.info("MailReach ping successful")
123
+ return result