dhisana 0.0.1.dev23__tar.gz → 0.0.1.dev25__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.
Files changed (84) hide show
  1. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/PKG-INFO +1 -1
  2. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/setup.py +1 -1
  3. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/schemas/sales.py +1 -1
  4. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/apollo_tools.py +1 -1
  5. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/compose_cadence.py +34 -27
  6. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/compose_three_step_workflow.py +1 -1
  7. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/compose_workflow.py +11 -11
  8. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/create_list_from_sales_navigator.py +2 -2
  9. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/create_smart_list.py +6 -6
  10. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/enrich_lead_information.py +4 -4
  11. dhisana-0.0.1.dev25/src/dhisana/utils/generate_linkedin_response_message.py +160 -0
  12. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/hubspot_crm_tools.py +4 -4
  13. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/proxy_curl_tools.py +12 -12
  14. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/sales_navigator_crawler.py +9 -9
  15. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/serpapi_search_tools.py +6 -6
  16. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana.egg-info/PKG-INFO +1 -1
  17. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana.egg-info/SOURCES.txt +1 -0
  18. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/README.md +0 -0
  19. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/pyproject.toml +0 -0
  20. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/setup.cfg +0 -0
  21. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/__init__.py +0 -0
  22. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/cli/__init__.py +0 -0
  23. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/cli/cli.py +0 -0
  24. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/cli/datasets.py +0 -0
  25. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/cli/models.py +0 -0
  26. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/cli/predictions.py +0 -0
  27. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/schemas/__init__.py +0 -0
  28. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/schemas/common.py +0 -0
  29. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/ui/__init__.py +0 -0
  30. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/ui/components.py +0 -0
  31. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/__init__.py +0 -0
  32. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/agent_task.py +0 -0
  33. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/agent_tools.py +0 -0
  34. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/assistant_tool_tag.py +0 -0
  35. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/built_with_api_tools.py +0 -0
  36. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/cache_output_tools.py +0 -0
  37. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/check_email_validity_tools.py +0 -0
  38. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/check_for_intent_signal.py +0 -0
  39. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/check_linkedin_url_validity.py +0 -0
  40. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/clay_tools.py +0 -0
  41. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/company_utils.py +0 -0
  42. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/compose_salesnav_query.py +0 -0
  43. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/compose_search_query.py +0 -0
  44. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/composite_tools.py +0 -0
  45. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/dataframe_tools.py +0 -0
  46. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/domain_parser.py +0 -0
  47. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/extract_email_content_for_llm.py +0 -0
  48. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/g2_tools.py +0 -0
  49. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/generate_email.py +0 -0
  50. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/generate_email_response.py +0 -0
  51. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/generate_flow.py +0 -0
  52. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/generate_linkedin_connect_message.py +0 -0
  53. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/generate_structured_output_internal.py +0 -0
  54. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/google_custom_search.py +0 -0
  55. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/google_workspace_tools.py +0 -0
  56. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/hubspot_clearbit.py +0 -0
  57. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/instantly_tools.py +0 -0
  58. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/linkedin_crawler.py +0 -0
  59. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/lusha_tools.py +0 -0
  60. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/openai_assistant_and_file_utils.py +0 -0
  61. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/openai_helpers.py +0 -0
  62. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/openapi_spec_to_tools.py +0 -0
  63. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/openapi_tool/__init__.py +0 -0
  64. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/openapi_tool/api_models.py +0 -0
  65. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/openapi_tool/convert_openai_spec_to_tool.py +0 -0
  66. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/openapi_tool/openapi_tool.py +0 -0
  67. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/python_function_to_tools.py +0 -0
  68. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/research_lead.py +0 -0
  69. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/salesforce_crm_tools.py +0 -0
  70. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/sendgrid_tools.py +0 -0
  71. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/trasform_json.py +0 -0
  72. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/web_download_parse_tools.py +0 -0
  73. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/workflow_code_model.py +0 -0
  74. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/utils/zoominfo_tools.py +0 -0
  75. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/workflow/__init__.py +0 -0
  76. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/workflow/agent.py +0 -0
  77. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/workflow/flow.py +0 -0
  78. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/workflow/task.py +0 -0
  79. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana/workflow/test.py +0 -0
  80. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana.egg-info/dependency_links.txt +0 -0
  81. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana.egg-info/entry_points.txt +0 -0
  82. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana.egg-info/requires.txt +0 -0
  83. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/src/dhisana.egg-info/top_level.txt +0 -0
  84. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev25}/tests/test_agent_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: dhisana
3
- Version: 0.0.1.dev23
3
+ Version: 0.0.1.dev25
4
4
  Summary: A Python SDK for Dhisana AI Platform
5
5
  Home-page: https://github.com/dhisana-ai/dhisana-python-sdk
6
6
  Author: Admin
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name='dhisana',
5
- version='0.0.1-dev23',
5
+ version='0.0.1-dev25',
6
6
  description='A Python SDK for Dhisana AI Platform',
7
7
  author='Admin',
8
8
  author_email='contact@dhisana.ai',
@@ -243,7 +243,7 @@ class HubSpotLeadInformation(BaseModel):
243
243
  lead_location: str = Field("", description="Location of the lead")
244
244
  organization_name: str = Field("", description="Current Company where lead works")
245
245
  organization_website: str = Field("", description="Current Company website of the lead")
246
- company_linkedin_url: str = Field("", description="Company LinkedIn URL")
246
+ organization_linkedin_url : str = Field("", description="Company LinkedIn URL")
247
247
  # additional_properties is dict of dict-of-strings.
248
248
  # We store all unmapped HubSpot fields as string => string.
249
249
  additional_properties: Dict[str, Dict[str, str]] = Field(
@@ -582,7 +582,7 @@ async def enrich_user_info_with_apollo(input_user_properties, tool_config):
582
582
  if person_data.get("organization") and person_data["organization"].get("name"):
583
583
  input_user_properties["organization_name"] = person_data["organization"]["name"]
584
584
  if person_data.get("organization") and person_data["organization"].get("linkedin_url"):
585
- input_user_properties["company_linkedin_url"] = person_data["organization"]["linkedin_url"]
585
+ input_user_properties["organization_linkedin_url "] = person_data["organization"]["linkedin_url"]
586
586
  if person_data.get("organization") and person_data["organization"].get("website_url"):
587
587
  input_user_properties["organization_website"] = person_data["organization"]["website_url"]
588
588
  if person_data.get("headline") and not input_user_properties.get("summary_about_lead"):
@@ -2,6 +2,7 @@ import json
2
2
  import logging
3
3
  from typing import Any, Dict, List, Optional, Tuple
4
4
 
5
+ from pydantic import BaseModel
5
6
 
6
7
  from dhisana.utils.generate_structured_output_internal import get_structured_output_with_o1
7
8
  from dhisana.utils.workflow_code_model import WorkflowPythonCode
@@ -10,7 +11,7 @@ from dhisana.utils.workflow_code_model import WorkflowPythonCode
10
11
  logging.basicConfig(level=logging.INFO)
11
12
  logger = logging.getLogger(__name__)
12
13
 
13
-
14
+
14
15
  async def generate_code_for_campaign_cadence(
15
16
  english_description: str,
16
17
  tool_config: Optional[List[Dict[str, Any]]] = None
@@ -19,25 +20,25 @@ async def generate_code_for_campaign_cadence(
19
20
  Generate a workflow code (Python code) from an English description.
20
21
 
21
22
  The function produced will be named:
22
- async def run_campaing_cadence(input_leads, tool_config) -> str
23
+ async def run_campaing_cadence(input_leads, campaign_context, tool_config) -> str
23
24
  Returns: (response_dict, status_string)
24
25
  """
25
26
  system_message = (
26
27
  "You are a helpful AI assistant and an expert Python coder. "
27
28
  "Convert the English description provided by the user into an executable Python function named "
28
29
  "'run_campaing_cadence', with the signature:\n"
29
- " async def run_campaing_cadence(input_leads, tool_config) -> str\n"
30
+ " async def run_campaing_cadence(input_leads, campaign_context, tool_config) -> str\n"
30
31
  "The function should iterate over 'input_leads' and perform the required LinkedIn actions via execute_task. "
31
32
  "Valid commands include: "
32
- " view_linked_in_profile, send_connection_request, like_recent_post, send_message, get_current_messages, save_lead.\n"
33
+ " view_linked_in_profile, send_connection_request, like_recent_post, send_linkedin_message, get_current_messages, save_lead.\n"
33
34
  "Return 'SUCCESS' or 'ERROR' as a string."
34
35
  )
35
36
 
36
37
  example_of_workflow_code = (
37
38
  '''
38
- async def run_campaing_cadence(input_leads, tool_config):
39
+ async def run_campaing_cadence(input_leads, campaign_context, tool_config):
39
40
  """
40
- Example skeleton. Run a linkedin campaign cadence with actions like view_profile, send_message etc.
41
+ Example skeleton. Run a linkedin campaign cadence with actions like view_profile, send_linkedin_message etc.
41
42
  Returns "SUCCESS" or "ERROR".
42
43
  """
43
44
 
@@ -56,7 +57,7 @@ async def generate_code_for_campaign_cadence(
56
57
  MAX_ACTIONS = {
57
58
  "view_profile": 30,
58
59
  "send_connection_request": 10,
59
- "send_message": 10,
60
+ "send_linkedin_message": 10,
60
61
  "like_post": 10
61
62
  }
62
63
 
@@ -64,7 +65,7 @@ async def generate_code_for_campaign_cadence(
64
65
  for lead in input_leads[:MAX_ACTIONS["view_profile"]]:
65
66
  await execute_task(
66
67
  command_name="view_linked_in_profile",
67
- command_args={"salesnav_url_profile": lead.get("lead_salesnav_url")},
68
+ command_args={"salesnav_url_profile": lead.get("user_linkedin_salesnav_url")},
68
69
  tool_config=tool_config
69
70
  )
70
71
  logger.debug("Viewed profile: %s", lead.get("full_name"))
@@ -73,23 +74,23 @@ async def generate_code_for_campaign_cadence(
73
74
  for lead in input_leads[:MAX_ACTIONS["send_connection_request"]]:
74
75
  await execute_task(
75
76
  command_name="send_connection_request",
76
- command_args={"salesnav_url_profile": lead.get("lead_salesnav_url")},
77
+ command_args={
78
+ "salesnav_url_profile": lead.get("user_linkedin_salesnav_url"),
79
+ "lead_info": lead,
80
+ "context": campaign_context
81
+ },
77
82
  tool_config=tool_config
78
83
  )
79
84
  logger.debug("Sent connection request: %s", lead.get("full_name"))
80
85
 
81
86
  # Send messages
82
- for lead in input_leads[:MAX_ACTIONS["send_message"]]:
83
- message_to_send = await get_personalized_linkedin_message(
84
- lead_info=lead,
85
- outreach_context="Hello from my LinkedIn automation!",
86
- tool_config=tool_config
87
- )
87
+ for lead in input_leads[:MAX_ACTIONS["send_linkedin_message"]]:
88
88
  await execute_task(
89
- command_name="send_message",
89
+ command_name="send_linkedin_message",
90
90
  command_args={
91
- "salesnav_url_profile": lead.get("lead_salesnav_url"),
92
- "message": message_to_send
91
+ "salesnav_url_profile": lead.get("user_linkedin_salesnav_url"),
92
+ "lead_info": lead,
93
+ "context": campaign_context
93
94
  },
94
95
  tool_config=tool_config
95
96
  )
@@ -99,7 +100,7 @@ async def generate_code_for_campaign_cadence(
99
100
  for lead in input_leads[:MAX_ACTIONS["like_post"]]:
100
101
  await execute_task(
101
102
  command_name="like_recent_post",
102
- command_args={"salesnav_url_profile": lead.get("lead_salesnav_url")},
103
+ command_args={"salesnav_url_profile": lead.get("user_linkedin_salesnav_url")},
103
104
  tool_config=tool_config
104
105
  )
105
106
  logger.debug("Liked recent post for lead: %s", lead.get("full_name"))
@@ -115,7 +116,7 @@ async def generate_code_for_campaign_cadence(
115
116
 
116
117
  user_prompt = f"""
117
118
  {system_message}
118
- "Make sure the imports are present within the function definition itself. Make sure the logging library is imported and logger defined withing the function. "
119
+ "Make sure the imports are present within the function definition itself. Make sure the logging library is imported and logger defined within the function. "
119
120
  The user wants to generate Python code that does this:
120
121
 
121
122
  \"{english_description}\"
@@ -124,7 +125,7 @@ async def generate_code_for_campaign_cadence(
124
125
  {example_of_workflow_code}
125
126
 
126
127
  The final code must be valid Python, and must produce:
127
- async def run_campaing_cadence(input_leads, tool_config) -> str
128
+ async def run_campaing_cadence(input_leads, campaign_context, tool_config) -> str
128
129
 
129
130
  Return your final output as valid JSON with:
130
131
  {{
@@ -136,8 +137,13 @@ async def generate_code_for_campaign_cadence(
136
137
  )
137
138
  return response.model_dump(), status
138
139
 
140
+
141
+ class CampaignContext(BaseModel):
142
+ instructions: str
143
+
144
+
139
145
  async def generate_campaign_cadence_workflow_and_execute(
140
- user_query: str,
146
+ instructions: str,
141
147
  input_leads: List[Dict[str, Any]],
142
148
  tool_config: Optional[List[Dict[str, Any]]] = None
143
149
  ) -> str:
@@ -149,7 +155,7 @@ async def generate_campaign_cadence_workflow_and_execute(
149
155
  JSON string describing success or error.
150
156
  """
151
157
  response, status = await generate_code_for_campaign_cadence(
152
- user_query, tool_config=tool_config
158
+ instructions, tool_config=tool_config
153
159
  )
154
160
 
155
161
  # Check if we have a successful code generation
@@ -170,12 +176,13 @@ async def generate_campaign_cadence_workflow_and_execute(
170
176
  if campaign_fn is None:
171
177
  raise RuntimeError("No 'run_campaing_cadence' function found in generated code.")
172
178
 
173
- # Now run the async function with the leads
174
- async def run_campaign(leads, cfg):
175
- return await campaign_fn(leads, cfg)
179
+ # Helper to call the newly generated async function
180
+ async def run_campaign(leads, campaign_context, cfg):
181
+ return await campaign_fn(leads, campaign_context, cfg)
176
182
 
177
183
  try:
178
- result = await run_campaign(input_leads, tool_config)
184
+ campaign_context = CampaignContext(instructions=instructions)
185
+ result = await run_campaign(input_leads, campaign_context, tool_config)
179
186
  except Exception as e:
180
187
  logger.exception("Error occurred while running run_campaing_cadence.")
181
188
  return json.dumps({"status": "ERROR", "error": str(e)})
@@ -196,7 +196,7 @@ async def generate_three_step_workflow_execute(
196
196
  # generate_campaign_cadence_workflow_and_execute might return JSON:
197
197
  # { "status": "SUCCESS", "result": "Campaign done" } or similar
198
198
  result_str = await generate_campaign_cadence_workflow_and_execute(
199
- user_query=step_3_instructions,
199
+ instructions=step_3_instructions,
200
200
  input_leads=leads_list,
201
201
  tool_config=tool_config
202
202
  )
@@ -42,7 +42,7 @@ async def generate_workflow_code(
42
42
  "view_linked_in_profile",
43
43
  "send_connection_request",
44
44
  "like_recent_post",
45
- "send_message",
45
+ "send_linkedin_message",
46
46
  "get_current_messages",
47
47
  ]
48
48
 
@@ -79,8 +79,8 @@ async def custom_workflow(tool_config):
79
79
  if not leads:
80
80
  return "SUCCESS"
81
81
 
82
- # Filter and deduplicate leads with valid lead_salesnav_url
83
- unique_leads = {lead['lead_salesnav_url']: lead for lead in leads if lead.get("lead_salesnav_url")}
82
+ # Filter and deduplicate leads with valid user_linkedin_salesnav_url
83
+ unique_leads = {lead['user_linkedin_salesnav_url']: lead for lead in leads if lead.get("user_linkedin_salesnav_url")}
84
84
  unique_leads = list(unique_leads.values())
85
85
  if not unique_leads:
86
86
  return "SUCCESS"
@@ -128,37 +128,37 @@ async def custom_workflow(tool_config):
128
128
  MAX_ACTIONS = {
129
129
  "view_profile": 30,
130
130
  "send_connection_request": 10,
131
- "send_message": 10,
131
+ "send_linkedin_message": 10,
132
132
  "like_post": 10
133
133
  }
134
134
 
135
135
  for lead in qualified_leads[:MAX_ACTIONS["view_profile"]]:
136
136
  await execute_task(
137
137
  command_name="view_linked_in_profile",
138
- command_args={"salesnav_url_profile": lead.get("lead_salesnav_url")},
138
+ command_args={"salesnav_url_profile": lead.get("user_linkedin_salesnav_url")},
139
139
  tool_config=tool_config
140
140
  )
141
141
 
142
142
  for lead in qualified_leads[:MAX_ACTIONS["send_connection_request"]]:
143
143
  await execute_task(
144
144
  command_name="send_connection_request",
145
- command_args={"salesnav_url_profile": lead.get("lead_salesnav_url")},
145
+ command_args={"salesnav_url_profile": lead.get("user_linkedin_salesnav_url")},
146
146
  tool_config=tool_config
147
147
  )
148
148
 
149
- for lead in qualified_leads[:MAX_ACTIONS["send_message"]]:
149
+ for lead in qualified_leads[:MAX_ACTIONS["send_linkedin_message"]]:
150
150
  context = "<< Fill in context that can be used to generate message>>" # Fill in campaign context in which the message is being sent
151
151
  message_to_send = await get_personalized_linkedin_message(lead_info=lead, outreach_context=context, tool_config=tool_config)
152
152
  await execute_task(
153
- command_name="send_message",
154
- command_args={"salesnav_url_profile": lead.get("lead_salesnav_url"), "message": message_to_send},
153
+ command_name="send_linkedin_message",
154
+ command_args={"salesnav_url_profile": lead.get("user_linkedin_salesnav_url"), "message": message_to_send},
155
155
  tool_config=tool_config
156
156
  )
157
157
 
158
158
  for lead in qualified_leads[:MAX_ACTIONS["like_post"]]:
159
159
  await execute_task(
160
160
  command_name="like_recent_post",
161
- command_args={"salesnav_url_profile": lead.get("lead_salesnav_url")},
161
+ command_args={"salesnav_url_profile": lead.get("user_linkedin_salesnav_url")},
162
162
  tool_config=tool_config
163
163
  )
164
164
 
@@ -181,7 +181,7 @@ async def custom_workflow(tool_config):
181
181
 
182
182
  Each lead returned has the following fields in the dicionary:
183
183
 
184
- full_name, first_name, last_name, email, lead_salesnav_url, company_salesnav_url,
184
+ full_name, first_name, last_name, email, user_linkedin_salesnav_url, organization_linkedin_salesnav_url ,
185
185
  user_linkedin_url, primary_domain_of_organization, job_title, phone, headline,
186
186
  lead_location, organization_name, organization_website, summary_about_lead, keywords,
187
187
  number_of_linkedin_connections
@@ -99,7 +99,7 @@ async def generate_sales_nav_list_workflow_code(
99
99
  # Deduplicate leads
100
100
  unique_leads = {}
101
101
  for lead in leads:
102
- salesnav_url = lead.get("lead_salesnav_url")
102
+ salesnav_url = lead.get("user_linkedin_salesnav_url")
103
103
  if salesnav_url:
104
104
  unique_leads[salesnav_url] = lead
105
105
 
@@ -173,7 +173,7 @@ async def generate_sales_nav_list_workflow_code(
173
173
  If user has provided a Sales Navigator URL set to to user_input_salesnav_url in the code and pass as input to generate_salesnav_people_search_url.
174
174
 
175
175
  Each lead returned has at least:
176
- full_name, first_name, last_name, email, lead_salesnav_url, company_salesnav_url,
176
+ full_name, first_name, last_name, email, user_linkedin_salesnav_url, organization_linkedin_salesnav_url ,
177
177
  user_linkedin_url, primary_domain_of_organization, job_title, phone, headline,
178
178
  lead_location, organization_name, organization_website, summary_about_lead, keywords,
179
179
  number_of_linkedin_connections
@@ -55,23 +55,23 @@ async def generate_smart_list_creation_code(
55
55
  try:
56
56
  logger.info("Starting custom_workflow execution")
57
57
 
58
- # Step 1 : Deduplicate leads by user_linkedin_url or lead_salesnav_url
58
+ # Step 1 : Deduplicate leads by user_linkedin_url or user_linkedin_salesnav_url
59
59
  leads_with_linkedin = {}
60
60
  leads_with_salesnav = {}
61
61
 
62
62
  for lead in input_leads_list:
63
63
  user_linkedin_url = lead.get("user_linkedin_url")
64
- lead_salesnav_url = lead.get("lead_salesnav_url")
64
+ user_linkedin_salesnav_url = lead.get("user_linkedin_salesnav_url")
65
65
  if user_linkedin_url:
66
66
  leads_with_linkedin[user_linkedin_url] = lead
67
- elif lead_salesnav_url:
68
- leads_with_salesnav[lead_salesnav_url] = lead
67
+ elif user_linkedin_salesnav_url:
68
+ leads_with_salesnav[user_linkedin_salesnav_url] = lead
69
69
 
70
70
  unique_leads = list(leads_with_linkedin.values()) + list(leads_with_salesnav.values())
71
71
  logger.info("Unique leads after deduplication: %d", len(unique_leads))
72
72
 
73
73
  if not unique_leads:
74
- logger.warning("No leads with valid 'user_linkedin_url' or 'lead_salesnav_url'; returning SUCCESS.")
74
+ logger.warning("No leads with valid 'user_linkedin_url' or 'user_linkedin_salesnav_url'; returning SUCCESS.")
75
75
  return "SUCCESS", []
76
76
 
77
77
  # Step 2 : Qulify the leads based on intent signals by calling check_for_intent_signal
@@ -123,7 +123,7 @@ async def generate_smart_list_creation_code(
123
123
  {system_message}
124
124
  Do the following step by step:
125
125
  1. Think on how the user wants to qualify the leads.
126
- 2. Deduplicate the leads based on user_linkedin_url or lead_salesnav_url as given in the example.
126
+ 2. Deduplicate the leads based on user_linkedin_url or user_linkedin_salesnav_url as given in the example.
127
127
  3. Fill in the right description on what is the qualification criteria and call check_for_intent_signal with signal_to_look_for_in_plan_english having qualification criteria.
128
128
  4. Do not make up any other logic to filter in python for intent signals. Use check_for_intent_signal to qualify the leads.
129
129
  5. Sort the qualified leads by intent_score in descending order.
@@ -15,7 +15,7 @@ from dhisana.utils.proxy_curl_tools import (
15
15
  enrich_user_info_with_proxy_curl,
16
16
  )
17
17
  from dhisana.utils.serpapi_search_tools import (
18
- find_company_linkedin_url_with_google_search,
18
+ find_organization_linkedin_url_with_google_search,
19
19
  find_user_linkedin_url_google,
20
20
  get_company_domain_from_google_search,
21
21
  )
@@ -153,7 +153,7 @@ async def enrich_organization_info_from_name(
153
153
  ) -> None:
154
154
  """
155
155
  Given a dictionary (treated like a CSV row) containing 'organization_name',
156
- 'company_linkedin_url', and 'website' keys, enrich the row only if the
156
+ 'organization_linkedin_url ', and 'website' keys, enrich the row only if the
157
157
  domain and website are currently empty.
158
158
 
159
159
  :param row: Dictionary representing a lead or company record.
@@ -162,7 +162,7 @@ async def enrich_organization_info_from_name(
162
162
  """
163
163
  org_name_key = "organization_name"
164
164
  org_domain_key = "primary_domain_of_organization"
165
- linkedin_url_key = "company_linkedin_url"
165
+ linkedin_url_key = "organization_linkedin_url "
166
166
  website_key = "website"
167
167
 
168
168
  org_name = (row.get(org_name_key) or "").strip()
@@ -182,7 +182,7 @@ async def enrich_organization_info_from_name(
182
182
  linkedin_url = row.get(linkedin_url_key, "").strip()
183
183
  if not linkedin_url:
184
184
  # TODO: If location detail is available, pass it to the search function.
185
- linkedin_url = await find_company_linkedin_url_with_google_search(
185
+ linkedin_url = await find_organization_linkedin_url_with_google_search(
186
186
  org_name,
187
187
  company_location="US",
188
188
  use_strict_check=use_strict_check,
@@ -0,0 +1,160 @@
1
+ from typing import Any, Dict, List, Optional
2
+ from pydantic import BaseModel
3
+ from dhisana.utils.generate_structured_output_internal import (
4
+ get_structured_output_with_o1
5
+ )
6
+
7
+ class LinkedInTriageResponse(BaseModel):
8
+ """
9
+ Model representing the structured response for a LinkedIn conversation triage.
10
+ - triage_status: "AUTOMATIC" or "REQUIRES_APPROVAL"
11
+ - triage_reason: Optional reason text if triage_status == "REQUIRES_APPROVAL"
12
+ - response_action_to_take: The recommended next action (e.g., SEND_REPLY, WAIT_TO_SEND, STOP_SENDING, etc.)
13
+ - response_message: The actual message (body) to be sent or used for approval.
14
+ """
15
+ triage_status: str # "AUTOMATIC" or "REQUIRES_APPROVAL"
16
+ triage_reason: Optional[str]
17
+ response_action_to_take: str
18
+ response_message: str
19
+
20
+
21
+ def cleanup_reply_linkedin_context(lead_info: Dict[str, Any]) -> Dict[str, Any]:
22
+ """
23
+ Clone or sanitize the lead info to remove unneeded or sensitive fields.
24
+ """
25
+ cleaned_lead_info = dict(lead_info) # Shallow copy
26
+
27
+ # Example of removing or masking unwanted fields
28
+ cleaned_lead_info.pop("task_ids", None)
29
+ cleaned_lead_info.pop("research_status", None)
30
+ # Add more fields you want to remove as needed
31
+
32
+ return cleaned_lead_info
33
+
34
+
35
+ async def generate_linkedin_response_message(
36
+ lead_info: Dict[str, Any],
37
+ outreach_context: str,
38
+ connect_template: str,
39
+ current_conversations: str,
40
+ additional_instructions: str,
41
+ tool_config: Optional[List[Dict]] = None
42
+ ) -> Dict[str, Any]:
43
+ """
44
+ Generate a triaged LinkedIn message, deciding if it can be sent automatically
45
+ or requires human approval. The final JSON structure must conform to
46
+ LinkedInTriageResponse (triage_status, triage_reason, response_action_to_take, response_message).
47
+
48
+ The content should be genuine, authentic, and professional, avoiding clichés
49
+ and heavy sales pushes. It should be respectful of user preferences (e.g., not
50
+ spamming a user who has shown disinterest).
51
+ """
52
+
53
+ # Possible actions for spam-avoidance, scheduling, and response
54
+ allowed_actions = [
55
+ "SEND_REPLY", # Normal: send reply
56
+ "WAIT_TO_SEND", # Wait because user was recently messaged
57
+ "STOP_SENDING", # No more messages to user
58
+ "SCHEDULE_MEETING", # If user requests a meeting
59
+ "FOLLOW_UP_LATER",
60
+ "NEED_MORE_INFO",
61
+ "OTHER"
62
+ ]
63
+
64
+ cleaned_lead_info = cleanup_reply_linkedin_context(lead_info)
65
+
66
+ # Incorporate the outreach context and guidelines directly in the prompt
67
+ prompt = f"""
68
+ You are a specialized LinkedIn assistant.
69
+
70
+ Your task:
71
+ 1. Analyze the current LinkedIn conversation.
72
+ 2. Inspect the user (lead) info, outreach campaign context, and any additional instructions.
73
+ 3. Decide whether to automatically send a reply or if human approval is needed (triage).
74
+ 4. If approval is needed, provide the reason.
75
+ 5. Choose one recommended next action from: {allowed_actions}.
76
+ 6. Provide a short LinkedIn message body that addresses the lead's conversation.
77
+
78
+ Connect Template (for reference):
79
+ {connect_template}
80
+
81
+ Outreach (Campaign) Context:
82
+ {outreach_context}
83
+
84
+ Additional Instructions (Triage Guidelines):
85
+ {additional_instructions}
86
+
87
+ Current Conversations:
88
+ {current_conversations}
89
+
90
+ Lead Info:
91
+ {cleaned_lead_info}
92
+
93
+ === IMPORTANT ANTI-SPAM AND RESPECT RULES ===
94
+ 1. If we have sent a message to the user within the past 24 hours and the user has not responded,
95
+ do NOT send another message right now. Instead, triage with "WAIT_TO_SEND".
96
+ 2. If we have sent more than 3 messages in total without any user response, do NOT send another message.
97
+ Instead, triage with "STOP_SENDING".
98
+ 3. If the user explicitly says "don't reply", "not interested", or any equivalent,
99
+ do NOT continue the thread. Triage with "STOP_SENDING".
100
+ 4. If the user has requested a meeting, triage as "AUTOMATIC" or "REQUIRES_APPROVAL"
101
+ (depending on complexity), and set response_action_to_take to "SCHEDULE_MEETING".
102
+ Craft a helpful response for scheduling.
103
+
104
+ === TONE AND STYLE RULES ===
105
+ - Your message must be genuine, authentic, and professional.
106
+ - Avoid clichés and spammy or overly aggressive sales pitches.
107
+ - Do not push sales in an unnatural way.
108
+
109
+ === OUTPUT FORMAT ===
110
+ Your final output must be valid JSON in this exact format:
111
+ {{
112
+ "triage_status": "AUTOMATIC" or "REQUIRES_APPROVAL",
113
+ "triage_reason": "<reason if REQUIRES_APPROVAL; else empty or null>",
114
+ "response_action_to_take": "<one of {allowed_actions}>",
115
+ "response_message": "<the new or reply message>"
116
+ }}
117
+ """
118
+
119
+ # Decide how to call your structured output function (based on vector store or not).
120
+ # We'll assume there's no vector store ID for LinkedIn. If you do have one, adapt similarly.
121
+ response, status = await get_structured_output_with_o1(
122
+ prompt=prompt,
123
+ response_format=LinkedInTriageResponse,
124
+ tool_config=tool_config
125
+ )
126
+
127
+ if status != 'SUCCESS':
128
+ raise Exception("Error in generating the triaged LinkedIn message.")
129
+
130
+ return response.model_dump()
131
+
132
+
133
+ async def get_linkedin_response_message(
134
+ lead_info: Dict[str, Any],
135
+ outreach_context: str,
136
+ current_conversations: str,
137
+ additional_instructions: str,
138
+ tool_config: Optional[List[Dict]] = None
139
+ ) -> str:
140
+ """
141
+ Returns only the 'response_message' from the triaged LinkedIn response.
142
+ This includes usage of the outreach (campaign) context to better tailor
143
+ the message to the lead.
144
+ """
145
+ connect_template = """
146
+ Hi <<first_name>>,
147
+ << message >>
148
+ Thanks,
149
+ << sender_name >>
150
+ """
151
+
152
+ triaged_response = await generate_linkedin_response_message(
153
+ lead_info=lead_info,
154
+ outreach_context=outreach_context,
155
+ connect_template=connect_template,
156
+ current_conversations=current_conversations,
157
+ additional_instructions=additional_instructions,
158
+ tool_config=tool_config
159
+ )
160
+ return triaged_response.get("response_message", "")
@@ -591,7 +591,7 @@ def transform_hubspot_contact_to_lead_info(
591
591
  Convert a raw HubSpot property dict into a LeadInformation object.
592
592
  - Maps known properties (e.g., "company" -> organization_name).
593
593
  - If any field value contains "linkedin.com/in/", sets user_linkedin_url.
594
- - If any field value contains "linkedin.com/company/", sets company_linkedin_url.
594
+ - If any field value contains "linkedin.com/company/", sets organization_linkedin_url .
595
595
  - Builds 'full_name' if missing.
596
596
  - Stores unmapped fields under result["additional_properties"]["hubspot_lead_information"] as strings.
597
597
  """
@@ -610,7 +610,7 @@ def transform_hubspot_contact_to_lead_info(
610
610
  "lead_location": "",
611
611
  "organization_name": "",
612
612
  "organization_website": "",
613
- "company_linkedin_url": "",
613
+ "organization_linkedin_url ": "",
614
614
  "additional_properties": {"hubspot_lead_information": {}},
615
615
  }
616
616
 
@@ -629,7 +629,7 @@ def transform_hubspot_contact_to_lead_info(
629
629
  result["user_linkedin_url"] = val_str
630
630
 
631
631
  if "linkedin.com/company/" in val_str and is_valid_url(val_str):
632
- result["company_linkedin_url"] = val_str
632
+ result["organization_linkedin_url "] = val_str
633
633
 
634
634
  # 3) Build "full_name" if not explicitly given
635
635
  if not result["full_name"]:
@@ -641,7 +641,7 @@ def transform_hubspot_contact_to_lead_info(
641
641
  # (Skip the ones we already consider "standard")
642
642
  standard_mapped_keys = set(HUBSPOT_TO_LEAD_MAPPING.keys()) | {
643
643
  "user_linkedin_url",
644
- "company_linkedin_url",
644
+ "organization_linkedin_url ",
645
645
  }
646
646
  for prop_key, prop_val in hubspot_contact_properties.items():
647
647
  if prop_key not in standard_mapped_keys:
@@ -435,7 +435,7 @@ async def find_matching_job_posting_proxy_curl(
435
435
  company_name: str,
436
436
  keywords_check: List[str],
437
437
  optional_keywords: List[str],
438
- company_linkedin_url: Optional[str] = None,
438
+ organization_linkedin_url : Optional[str] = None,
439
439
  tool_config: Optional[List[Dict]] = None
440
440
  ) -> List[str]:
441
441
  """
@@ -446,7 +446,7 @@ async def find_matching_job_posting_proxy_curl(
446
446
  company_name (str): The name of the company.
447
447
  keywords_check (List[str]): A list of keywords to include in the search.
448
448
  optinal_keywords (List[str]): A list of optional keywords to include in the search.
449
- company_linkedin_url (Optional[str]): The LinkedIn URL of the company.
449
+ organization_linkedin_url (Optional[str]): The LinkedIn URL of the company.
450
450
 
451
451
  Returns:
452
452
  List[str]: A list of job posting links.
@@ -502,10 +502,10 @@ async def find_matching_job_posting_proxy_curl(
502
502
 
503
503
  text = json.dumps(json_result).lower()
504
504
 
505
- if company_linkedin_url and json_result.get('company', {}) and json_result.get('company', {}).get('url', ''):
505
+ if organization_linkedin_url and json_result.get('company', {}) and json_result.get('company', {}).get('url', ''):
506
506
  result_url = json_result.get('company', {}).get('url', '').lower()
507
507
  result_path = urlparse(result_url).path
508
- company_path = urlparse(company_linkedin_url.lower()).path
508
+ company_path = urlparse(organization_linkedin_url .lower()).path
509
509
  company_match = result_path == company_path
510
510
  else:
511
511
  company_match = False
@@ -632,12 +632,12 @@ async def enrich_user_info_with_proxy_curl(input_user_properties, tool_config):
632
632
  input_user_properties["lead_location"] = combined.strip(", ")
633
633
 
634
634
  if experiences and len(experiences) > 0:
635
- company_linkedin_url = experiences[0].get("company_linkedin_profile_url", "")
636
- if company_linkedin_url:
637
- input_user_properties["company_linkedin_url"] = company_linkedin_url
635
+ organization_linkedin_url = experiences[0].get("company_linkedin_profile_url", "")
636
+ if organization_linkedin_url :
637
+ input_user_properties["organization_linkedin_url "] = organization_linkedin_url
638
638
  #TODO enrich company information optionally
639
- # if company_linkedin_url:
640
- # organization_data = await enrich_organization_info_from_proxycurl(organization_domain="", organization_linkedin_url=company_linkedin_url, tool_config=tool_config)
639
+ # if organization_linkedin_url :
640
+ # organization_data = await enrich_organization_info_from_proxycurl(organization_domain="", organization_linkedin_url=organization_linkedin_url , tool_config=tool_config)
641
641
  # if organization_data:
642
642
  # additional_props["proxy_curl_organization_data"] = organization_data
643
643
  # input_user_properties["website"] = organization_data.get("website", "")
@@ -650,9 +650,9 @@ async def enrich_user_info_with_proxy_curl(input_user_properties, tool_config):
650
650
 
651
651
  if experiences and len(experiences) > 1:
652
652
  previous_org = experiences[1]
653
- previous_company_linkedin_url = previous_org.get("company_linkedin_profile_url", "")
654
- if previous_company_linkedin_url:
655
- input_user_properties["previous_company_linkedin_url"] = previous_company_linkedin_url
653
+ previous_organization_linkedin_url = previous_org.get("company_linkedin_profile_url", "")
654
+ if previous_organization_linkedin_url :
655
+ input_user_properties["previous_organization_linkedin_url "] = previous_organization_linkedin_url
656
656
  input_user_properties["previous_organization_name"] = previous_org.get("company", "")
657
657
 
658
658
  first_matched = bool(
@@ -179,7 +179,7 @@ async def find_button_by_name(page: Page, button_name: str) -> Any:
179
179
  return b
180
180
  return None
181
181
 
182
- async def send_message(page: Page, command_args: Dict[str, Any]) -> Dict[str, Any]:
182
+ async def send_linkedin_message(page: Page, command_args: Dict[str, Any]) -> Dict[str, Any]:
183
183
  await goto_url(page, command_args)
184
184
 
185
185
  if "/sales/lead/" not in page.url:
@@ -368,7 +368,7 @@ async def extract_leads_from_current_page(page):
368
368
 
369
369
  # Initialize these if you want placeholders (as in JS)
370
370
  lead["user_linkedin_url"] = ""
371
- lead["company_linkedin_url"] = ""
371
+ lead["organization_linkedin_url "] = ""
372
372
 
373
373
  # About / Experience
374
374
  dt_elements = div.find_all("dt")
@@ -399,10 +399,10 @@ async def extract_leads_from_current_page(page):
399
399
  comma_index = linkedin_id.find(",")
400
400
  if comma_index != -1:
401
401
  linkedin_id = linkedin_id[:comma_index]
402
- lead["lead_salesnav_url"] = f"https://www.linkedin.com/sales/lead/{linkedin_id}"
402
+ lead["user_linkedin_salesnav_url"] = f"https://www.linkedin.com/sales/lead/{linkedin_id}"
403
403
  else:
404
404
  # Fallback if we didn't match or want to keep the full URL
405
- lead["lead_salesnav_url"] = f"https://www.linkedin.com{href}"
405
+ lead["user_linkedin_salesnav_url"] = f"https://www.linkedin.com{href}"
406
406
 
407
407
  company_link_el = div.select_one('a[href^="/sales/company/"]')
408
408
  if company_link_el:
@@ -415,9 +415,9 @@ async def extract_leads_from_current_page(page):
415
415
  comma_index = linkedin_id.find(",")
416
416
  if comma_index != -1:
417
417
  linkedin_id = linkedin_id[:comma_index]
418
- lead["company_salesnav_url"] = f"https://www.linkedin.com/sales/company/{linkedin_id}"
418
+ lead["organization_linkedin_salesnav_url "] = f"https://www.linkedin.com/sales/company/{linkedin_id}"
419
419
  else:
420
- lead["company_salesnav_url"] = f"https://www.linkedin.com{href}"
420
+ lead["organization_linkedin_salesnav_url "] = f"https://www.linkedin.com{href}"
421
421
 
422
422
  leads.append(lead)
423
423
 
@@ -573,7 +573,7 @@ async def extract_account_from_current_page(page: Page):
573
573
 
574
574
  # If we successfully copied the LinkedIn.com URL, place it into the dictionary
575
575
  if 'copied_text' in locals() and "linkedin.com/company/" in copied_text:
576
- company["company_linkedin_url"] = copied_text
576
+ company["organization_linkedin_url "] = copied_text
577
577
 
578
578
  return [company], "SUCCESS"
579
579
 
@@ -787,7 +787,7 @@ command_to_function_mapping = {
787
787
  "navigate_to_url": navigate_to_url,
788
788
  "view_linked_in_profile": view_linked_in_profile,
789
789
  "send_connection_request": send_connection_request,
790
- "send_message": send_message,
790
+ "send_linkedin_message": send_linkedin_message,
791
791
  "get_current_messages": get_current_messages,
792
792
  "extract_leads_information": extract_leads,
793
793
  "extract_lead_information": extract_lead,
@@ -989,7 +989,7 @@ def check_and_update_throttling_limits(task: Dict[str, Any]) -> Dict[str, Any]:
989
989
  "view_linked_in_profile": 500,
990
990
  "navigate_to_url": 500,
991
991
  "like_recent_post": 10,
992
- "send_message": 10
992
+ "send_linkedin_message": 10
993
993
  }
994
994
  limit = daily_limits.get(cmd_id, 25)
995
995
 
@@ -585,7 +585,7 @@ def extract_company_page(url: str) -> str:
585
585
 
586
586
 
587
587
  @assistant_tool
588
- async def find_company_linkedin_url_with_google_search(
588
+ async def find_organization_linkedin_url_with_google_search(
589
589
  company_name: str,
590
590
  company_location: Optional[str] = None,
591
591
  use_strict_check: bool = True,
@@ -702,7 +702,7 @@ async def get_company_website_from_linkedin_url(linkedin_url):
702
702
  async def find_job_postings_google_search(
703
703
  company_name: str,
704
704
  keywords_check: List[str],
705
- company_linkedin_url: Optional[str] = None,
705
+ organization_linkedin_url : Optional[str] = None,
706
706
  tool_config: Optional[List[Dict]] = None,
707
707
  ) -> List[str]:
708
708
  """
@@ -710,7 +710,7 @@ async def find_job_postings_google_search(
710
710
 
711
711
  Args:
712
712
  company_name (str): The name of the company.
713
- company_linkedin_url (Optional[str]): The LinkedIn URL of the company.
713
+ organization_linkedin_url (Optional[str]): The LinkedIn URL of the company.
714
714
  keywords_check (List[str]): A list of keywords to include in the search.
715
715
 
716
716
  Returns:
@@ -758,11 +758,11 @@ async def find_job_postings_google_search(
758
758
  # Extract all hrefs from the page
759
759
  page_links = [a.get('href') for a in soup.find_all('a', href=True)]
760
760
 
761
- # Check if company_linkedin_url and 'public_jobs_topcard-org-name' are in the page links
761
+ # Check if organization_linkedin_url and 'public_jobs_topcard-org-name' are in the page links
762
762
  company_match = False
763
- if company_linkedin_url:
763
+ if organization_linkedin_url :
764
764
  for page_link in page_links:
765
- if (page_link and company_linkedin_url in page_link and
765
+ if (page_link and organization_linkedin_url in page_link and
766
766
  'public_jobs_topcard-org-name' in page_link):
767
767
  company_match = True
768
768
  break
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: dhisana
3
- Version: 0.0.1.dev23
3
+ Version: 0.0.1.dev25
4
4
  Summary: A Python SDK for Dhisana AI Platform
5
5
  Home-page: https://github.com/dhisana-ai/dhisana-python-sdk
6
6
  Author: Admin
@@ -47,6 +47,7 @@ src/dhisana/utils/generate_email.py
47
47
  src/dhisana/utils/generate_email_response.py
48
48
  src/dhisana/utils/generate_flow.py
49
49
  src/dhisana/utils/generate_linkedin_connect_message.py
50
+ src/dhisana/utils/generate_linkedin_response_message.py
50
51
  src/dhisana/utils/generate_structured_output_internal.py
51
52
  src/dhisana/utils/google_custom_search.py
52
53
  src/dhisana/utils/google_workspace_tools.py
File without changes
File without changes