dhisana 0.0.1.dev23__tar.gz → 0.0.1.dev24__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.dev24}/PKG-INFO +1 -1
  2. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/setup.py +1 -1
  3. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/compose_cadence.py +31 -24
  4. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/compose_three_step_workflow.py +1 -1
  5. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/compose_workflow.py +4 -4
  6. dhisana-0.0.1.dev24/src/dhisana/utils/generate_linkedin_response_message.py +160 -0
  7. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/sales_navigator_crawler.py +3 -3
  8. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana.egg-info/PKG-INFO +1 -1
  9. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana.egg-info/SOURCES.txt +1 -0
  10. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/README.md +0 -0
  11. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/pyproject.toml +0 -0
  12. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/setup.cfg +0 -0
  13. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/__init__.py +0 -0
  14. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/cli/__init__.py +0 -0
  15. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/cli/cli.py +0 -0
  16. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/cli/datasets.py +0 -0
  17. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/cli/models.py +0 -0
  18. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/cli/predictions.py +0 -0
  19. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/schemas/__init__.py +0 -0
  20. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/schemas/common.py +0 -0
  21. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/schemas/sales.py +0 -0
  22. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/ui/__init__.py +0 -0
  23. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/ui/components.py +0 -0
  24. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/__init__.py +0 -0
  25. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/agent_task.py +0 -0
  26. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/agent_tools.py +0 -0
  27. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/apollo_tools.py +0 -0
  28. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/assistant_tool_tag.py +0 -0
  29. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/built_with_api_tools.py +0 -0
  30. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/cache_output_tools.py +0 -0
  31. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/check_email_validity_tools.py +0 -0
  32. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/check_for_intent_signal.py +0 -0
  33. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/check_linkedin_url_validity.py +0 -0
  34. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/clay_tools.py +0 -0
  35. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/company_utils.py +0 -0
  36. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/compose_salesnav_query.py +0 -0
  37. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/compose_search_query.py +0 -0
  38. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/composite_tools.py +0 -0
  39. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/create_list_from_sales_navigator.py +0 -0
  40. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/create_smart_list.py +0 -0
  41. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/dataframe_tools.py +0 -0
  42. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/domain_parser.py +0 -0
  43. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/enrich_lead_information.py +0 -0
  44. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/extract_email_content_for_llm.py +0 -0
  45. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/g2_tools.py +0 -0
  46. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/generate_email.py +0 -0
  47. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/generate_email_response.py +0 -0
  48. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/generate_flow.py +0 -0
  49. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/generate_linkedin_connect_message.py +0 -0
  50. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/generate_structured_output_internal.py +0 -0
  51. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/google_custom_search.py +0 -0
  52. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/google_workspace_tools.py +0 -0
  53. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/hubspot_clearbit.py +0 -0
  54. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/hubspot_crm_tools.py +0 -0
  55. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/instantly_tools.py +0 -0
  56. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/linkedin_crawler.py +0 -0
  57. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/lusha_tools.py +0 -0
  58. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/openai_assistant_and_file_utils.py +0 -0
  59. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/openai_helpers.py +0 -0
  60. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/openapi_spec_to_tools.py +0 -0
  61. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/openapi_tool/__init__.py +0 -0
  62. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/openapi_tool/api_models.py +0 -0
  63. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/openapi_tool/convert_openai_spec_to_tool.py +0 -0
  64. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/openapi_tool/openapi_tool.py +0 -0
  65. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/proxy_curl_tools.py +0 -0
  66. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/python_function_to_tools.py +0 -0
  67. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/research_lead.py +0 -0
  68. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/salesforce_crm_tools.py +0 -0
  69. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/sendgrid_tools.py +0 -0
  70. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/serpapi_search_tools.py +0 -0
  71. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/trasform_json.py +0 -0
  72. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/web_download_parse_tools.py +0 -0
  73. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/workflow_code_model.py +0 -0
  74. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/utils/zoominfo_tools.py +0 -0
  75. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/workflow/__init__.py +0 -0
  76. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/workflow/agent.py +0 -0
  77. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/workflow/flow.py +0 -0
  78. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/workflow/task.py +0 -0
  79. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana/workflow/test.py +0 -0
  80. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana.egg-info/dependency_links.txt +0 -0
  81. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana.egg-info/entry_points.txt +0 -0
  82. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana.egg-info/requires.txt +0 -0
  83. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/src/dhisana.egg-info/top_level.txt +0 -0
  84. {dhisana-0.0.1.dev23 → dhisana-0.0.1.dev24}/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.dev24
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-dev24',
6
6
  description='A Python SDK for Dhisana AI Platform',
7
7
  author='Admin',
8
8
  author_email='contact@dhisana.ai',
@@ -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
 
@@ -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("lead_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
91
  "salesnav_url_profile": lead.get("lead_salesnav_url"),
92
- "message": message_to_send
92
+ "lead_info": lead,
93
+ "context": campaign_context
93
94
  },
94
95
  tool_config=tool_config
95
96
  )
@@ -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
 
@@ -128,7 +128,7 @@ 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
 
@@ -146,11 +146,11 @@ async def custom_workflow(tool_config):
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",
153
+ command_name="send_linkedin_message",
154
154
  command_args={"salesnav_url_profile": lead.get("lead_salesnav_url"), "message": message_to_send},
155
155
  tool_config=tool_config
156
156
  )
@@ -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", "")
@@ -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:
@@ -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
 
@@ -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.dev24
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