dhisana 0.0.1.dev254__tar.gz → 0.0.1.dev256__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 (121) hide show
  1. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/PKG-INFO +1 -1
  2. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/setup.py +1 -1
  3. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/generate_email_response.py +132 -117
  4. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/test_connect.py +48 -0
  5. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana.egg-info/PKG-INFO +1 -1
  6. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/README.md +0 -0
  7. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/pyproject.toml +0 -0
  8. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/setup.cfg +0 -0
  9. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/__init__.py +0 -0
  10. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/cli/__init__.py +0 -0
  11. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/cli/cli.py +0 -0
  12. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/cli/datasets.py +0 -0
  13. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/cli/models.py +0 -0
  14. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/cli/predictions.py +0 -0
  15. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/schemas/__init__.py +0 -0
  16. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/schemas/common.py +0 -0
  17. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/schemas/sales.py +0 -0
  18. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/ui/__init__.py +0 -0
  19. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/ui/components.py +0 -0
  20. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/__init__.py +0 -0
  21. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/add_mapping.py +0 -0
  22. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/agent_tools.py +0 -0
  23. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/apollo_tools.py +0 -0
  24. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/assistant_tool_tag.py +0 -0
  25. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/built_with_api_tools.py +0 -0
  26. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/cache_output_tools.py +0 -0
  27. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/cache_output_tools_local.py +0 -0
  28. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/check_email_validity_tools.py +0 -0
  29. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/check_for_intent_signal.py +0 -0
  30. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/check_linkedin_url_validity.py +0 -0
  31. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/clay_tools.py +0 -0
  32. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/clean_properties.py +0 -0
  33. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/company_utils.py +0 -0
  34. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/compose_salesnav_query.py +0 -0
  35. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/compose_search_query.py +0 -0
  36. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/compose_three_step_workflow.py +0 -0
  37. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/composite_tools.py +0 -0
  38. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/dataframe_tools.py +0 -0
  39. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/domain_parser.py +0 -0
  40. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/email_body_utils.py +0 -0
  41. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/email_parse_helpers.py +0 -0
  42. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/email_provider.py +0 -0
  43. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/enrich_lead_information.py +0 -0
  44. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/extract_email_content_for_llm.py +0 -0
  45. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/fetch_openai_config.py +0 -0
  46. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/field_validators.py +0 -0
  47. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/g2_tools.py +0 -0
  48. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/generate_content.py +0 -0
  49. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/generate_custom_message.py +0 -0
  50. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/generate_email.py +0 -0
  51. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/generate_flow.py +0 -0
  52. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/generate_leads_salesnav.py +0 -0
  53. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/generate_linkedin_connect_message.py +0 -0
  54. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/generate_linkedin_response_message.py +0 -0
  55. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/generate_structured_output_internal.py +0 -0
  56. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/google_custom_search.py +0 -0
  57. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/google_oauth_tools.py +0 -0
  58. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/google_workspace_tools.py +0 -0
  59. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/hubspot_clearbit.py +0 -0
  60. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/hubspot_crm_tools.py +0 -0
  61. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/instantly_tools.py +0 -0
  62. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/linkedin_crawler.py +0 -0
  63. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/lusha_tools.py +0 -0
  64. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/mailgun_tools.py +0 -0
  65. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/mailreach_tools.py +0 -0
  66. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/microsoft365_tools.py +0 -0
  67. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/openai_assistant_and_file_utils.py +0 -0
  68. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/openai_helpers.py +0 -0
  69. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/openapi_spec_to_tools.py +0 -0
  70. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/openapi_tool/__init__.py +0 -0
  71. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/openapi_tool/api_models.py +0 -0
  72. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/openapi_tool/convert_openai_spec_to_tool.py +0 -0
  73. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/openapi_tool/openapi_tool.py +0 -0
  74. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/parse_linkedin_messages_txt.py +0 -0
  75. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/profile.py +0 -0
  76. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/proxy_curl_tools.py +0 -0
  77. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/proxycurl_search_leads.py +0 -0
  78. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/python_function_to_tools.py +0 -0
  79. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/research_lead.py +0 -0
  80. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/sales_navigator_crawler.py +0 -0
  81. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/salesforce_crm_tools.py +0 -0
  82. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/search_router.py +0 -0
  83. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/search_router_jobs.py +0 -0
  84. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/sendgrid_tools.py +0 -0
  85. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/serarch_router_local_business.py +0 -0
  86. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/serpapi_additional_tools.py +0 -0
  87. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/serpapi_google_jobs.py +0 -0
  88. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/serpapi_google_search.py +0 -0
  89. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/serpapi_local_business_search.py +0 -0
  90. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/serpapi_search_tools.py +0 -0
  91. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/serperdev_google_jobs.py +0 -0
  92. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/serperdev_local_business.py +0 -0
  93. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/serperdev_search.py +0 -0
  94. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/smtp_email_tools.py +0 -0
  95. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/trasform_json.py +0 -0
  96. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/web_download_parse_tools.py +0 -0
  97. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/workflow_code_model.py +0 -0
  98. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/utils/zoominfo_tools.py +0 -0
  99. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/workflow/__init__.py +0 -0
  100. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/workflow/agent.py +0 -0
  101. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/workflow/flow.py +0 -0
  102. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/workflow/task.py +0 -0
  103. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana/workflow/test.py +0 -0
  104. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana.egg-info/SOURCES.txt +0 -0
  105. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana.egg-info/dependency_links.txt +0 -0
  106. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana.egg-info/entry_points.txt +0 -0
  107. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana.egg-info/requires.txt +0 -0
  108. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/src/dhisana.egg-info/top_level.txt +0 -0
  109. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/tests/test_agent_tools.py +0 -0
  110. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/tests/test_apollo_company_search.py +0 -0
  111. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/tests/test_apollo_lead_search.py +0 -0
  112. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/tests/test_connectivity.py +0 -0
  113. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/tests/test_email_body_utils.py +0 -0
  114. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/tests/test_google_document.py +0 -0
  115. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/tests/test_hubspot_call_logs.py +0 -0
  116. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/tests/test_linkedin_serper.py +0 -0
  117. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/tests/test_mailreach.py +0 -0
  118. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/tests/test_mcp_connectivity.py +0 -0
  119. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/tests/test_proxycurl_get_company_search_id.py +0 -0
  120. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/tests/test_proxycurl_job_count.py +0 -0
  121. {dhisana-0.0.1.dev254 → dhisana-0.0.1.dev256}/tests/test_structured_output_with_mcp.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dhisana
3
- Version: 0.0.1.dev254
3
+ Version: 0.0.1.dev256
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-dev254',
5
+ version='0.0.1-dev256',
6
6
  description='A Python SDK for Dhisana AI Platform',
7
7
  author='Admin',
8
8
  author_email='contact@dhisana.ai',
@@ -246,129 +246,144 @@ async def generate_inbound_email_response_copy(
246
246
  sender_data = cleaned_context.sender_info or SenderInfo()
247
247
 
248
248
  prompt = f"""
249
- You are a specialized email assistant.
250
- Your task is to analyze the user's email thread, the user/company info,
251
- and the provided triage guidelines to craft an appropriate response.
252
-
253
- Follow these instructions to generate the reply:
254
- {variation}
255
-
256
- 1. Email thread or conversation to respond to:
257
- {[thread_item.model_dump() for thread_item in cleaned_context.current_conversation_context.current_email_thread]
258
- if cleaned_context.current_conversation_context.current_email_thread else []}
259
-
260
- 2) Lead Information:
261
- {lead_data.dict()}
262
-
263
- Sender Information:
264
- Full Name: {sender_data.sender_full_name or ''}
265
- First Name: {sender_data.sender_first_name or ''}
266
- Last Name: {sender_data.sender_last_name or ''}
267
- Bio: {sender_data.sender_bio or ''}
268
-
269
-
270
- 3. Campaign-specific triage guidelines (user overrides always win):
271
- {cleaned_context.campaign_context.email_triage_guidelines}
272
-
273
- -----------------------------------------------------------------
274
- Core decision logic
275
- -----------------------------------------------------------------
276
- • If the request is routine, non-sensitive, and clearly actionable
277
- → **triage_status = "AUTOMATIC"**.
278
- If the thread contains PII, finance, legal, or any sensitive/NSFW content
279
- → **triage_status = "END_CONVERSATION"** and give a concise **triage_reason**.
280
-
281
- 4. Choose exactly ONE of: {allowed_actions}
282
-
283
- -----------------------------------------------------------------
284
- Response best practices
285
- -----------------------------------------------------------------
286
- MAX 150 words, friendly & concise, single clear CTA.
287
- Begin with a thank-you, mirror the prospect’s wording briefly, then answer /
288
- propose next step.
289
- Never contradict, trash-talk, or disparage {campaign_context.lead_info.organization_name}.
290
- • Plain-text only – NO HTML tags (<a>, <b>, <i>, etc.).
291
- • If a link already exists in the inbound email, include it verbatim—do not re-wrap or shorten.
292
-
293
- Meeting & follow-up rules
294
- -------------------------
295
- 1. Let `meeting_offer_sent` = **true** if any earlier assistant message offered a
296
- meeting.
297
- 2. If First “Thanks / Sounds good” & *no* prior meeting offer
298
- **SEND_REPLY** asking for a 15-min call (≤150 words).
299
- 3. If Second non-committal reply *after* meeting_offer_sent, or explicit “not interested”
300
- **END_CONVERSATION**.
301
- 4. If prospect explicitly asks for times / requests your link
302
- **SCHEDULE_MEETING** and confirm or propose times.
303
- 5. If One unsolicited follow-up maximum; stop unless prospect re-engages.
304
-
305
- If you have not proposed a meeting even once in the thread, and the user response is polite acknowledgment then you MUST request for a meeting.
306
-
307
-
308
- Objections & info requests
309
- --------------------------
310
- Pricing / docs / case-studies request → **NEED_MORE_INFO**.
311
- • Budget, timing, or competitor concerns → **OBJECTION_RAISED**
312
- (acknowledge + one clarifying Q or concise value point).
313
- “Loop in {{colleague_name}}” **FORWARD_TO_OTHER_USER**.
314
-
315
- Unsubscribe & priority handling
316
- -------------------------------
317
- 1. “Unsubscribe / Remove me” → **UNSUBSCRIBE**
318
- 2. Clear lack of interest **NOT_INTERESTED**
319
- 3. Auto OOO reply **OOF_MESSAGE**
320
- 4. Explicit meeting request → **SCHEDULE_MEETING**
321
- 5. Otherwise follow the Meeting & follow-up rules above
322
- 6. Default **END_CONVERSATION**
323
-
324
- Style guard-rails
325
- -----------------
326
- • Plain language; no jargon or filler.
327
- Do **not** repeat previous messages verbatim.
328
- • Signature must include sender_first_name exactly as provided.
329
- • Check UNSUBSCRIBE / NOT_INTERESTED first before other triage.
330
-
331
- If you have not proposed a meeting even once in the thread, and the user response is polite acknowledgment then you MUST request for a meeting.
332
-
333
- Meeting ask template example:
334
- Hi {{lead_first_name}}, would you be open to a quick 15-min call to
335
- understand your use-case and share notes?
336
-
337
- • Competitor-stack mention template example:
338
- Hi {{lead_first_name}}, thanks for sharing your current stack. Would you be
339
- open to a 15-min call to explore where we can add value?
340
-
341
- Use conversational name for company name.
342
- Use conversational name when using lead first name.
343
- Do not use special characters or spaces when using lead’s first name.
344
- In the subject or body DO NOT include any HTML tags like <a>, <b>, <i>, etc.
345
- The body and subject should be in plain text.
346
- If there is a link provided in the email, use it as is; do not wrap it in any HTML tags.
347
- DO NOT make up information. Use only the information provided in the context and instructions.
348
- Do NOT repeat the same message sent to the user in the past.
349
- Keep the thread conversational and friendly as a good account executive would respond.
350
- Do NOT rehash/repeat the same previous message already sent. Keep the reply to the point.
351
- DO NOT try to spam users with multiple messages.
352
- Current date is: {current_date_iso}.
353
- DO NOT share any link to internal or made up document. You can attach or send any document.
354
- If the user is asking for any additional document END_CONVERSATION and let Account executive handle it.
355
- - Make sure the body text is well-formatted and that newline and carriage-return characters are correctly present and preserved in the message body.
356
- - Do Not use em dash in the generated output.
357
-
358
- Required JSON output
359
- --------------------
360
- {{
249
+ You are a B2B account executive replying to warm inbound or engaged leads.
250
+
251
+ Your goal is to sound natural, helpful, and human while following all triage,
252
+ compliance, and action rules below.
253
+
254
+ Write responses the way a strong AE would type them, not like a system message.
255
+
256
+ =====================================================
257
+ INPUT CONTEXT
258
+ =====================================================
259
+
260
+ 1) Email thread to respond to:
261
+ {[thread_item.model_dump() for thread_item in cleaned_context.current_conversation_context.current_email_thread]
262
+ if cleaned_context.current_conversation_context.current_email_thread else []}
263
+
264
+ 2) Lead information:
265
+ {lead_data.dict()}
266
+
267
+ Sender information:
268
+ - Full name: {sender_data.sender_full_name or ''}
269
+ - First name: {sender_data.sender_first_name or ''}
270
+ - Last name: {sender_data.sender_last_name or ''}
271
+ - Bio: {sender_data.sender_bio or ''}
272
+
273
+ 3) Campaign-specific triage guidelines
274
+ (User overrides always take priority):
275
+ {cleaned_context.campaign_context.email_triage_guidelines}
276
+
277
+ =====================================================
278
+ TRIAGE DECISION LOGIC
279
+ =====================================================
280
+
281
+ Decide first. Write second.
282
+
283
+ • Routine, non-sensitive, clearly actionable
284
+ triage_status = "AUTOMATIC"
285
+
286
+ PII, finance, legal, contracts, compliance, NSFW,
287
+ or document requests
288
+ triage_status = "END_CONVERSATION"
289
+ Include a brief triage_reason
290
+
291
+ =====================================================
292
+ ACTION SELECTION
293
+ =====================================================
294
+
295
+ Choose exactly ONE action from:
296
+ {allowed_actions}
297
+
298
+ Priority order:
299
+ 1. UNSUBSCRIBE
300
+ 2. NOT_INTERESTED
301
+ 3. OOF_MESSAGE
302
+ 4. SCHEDULE_MEETING
303
+ 5. FORWARD_TO_OTHER_USER
304
+ 6. NEED_MORE_INFO
305
+ 7. OBJECTION_RAISED
306
+ 8. SEND_REPLY
307
+ 9. END_CONVERSATION
308
+
309
+ =====================================================
310
+ HOW THE RESPONSE SHOULD SOUND
311
+ =====================================================
312
+
313
+ This is a warm lead. Assume positive intent.
314
+
315
+ Friendly, relaxed, and conversational
316
+ • Short sentences. Natural pacing.
317
+ One clear idea per paragraph
318
+ No sales pressure. No hype. No buzzwords.
319
+ Helpful first. Next step second.
320
+
321
+ Think:
322
+ "Thanks for reaching out. Happy to help."
323
+ Not:
324
+ "Thank you for your inquiry regarding..."
325
+
326
+ =====================================================
327
+ RESPONSE STRUCTURE (LOOSE, NOT FORMAL)
328
+ =====================================================
329
+
330
+ Typical flow:
331
+ 1. Quick thank-you or acknowledgement
332
+ 2. Briefly mirror what they said or asked
333
+ 3. Answer or clarify in plain language
334
+ 4. Suggest a simple next step, if appropriate
335
+
336
+ Do not force all steps if it feels unnatural.
337
+
338
+ =====================================================
339
+ HARD RULES
340
+ =====================================================
341
+
342
+ Plain text only. No HTML.
343
+ Do not repeat previous messages verbatim.
344
+ Do not invent information, pricing, links, or docs.
345
+ If a link exists in the inbound email, reuse it exactly.
346
+ Do not add new links or attachments.
347
+ If documents are requested, END_CONVERSATION.
348
+ Never contradict or disparage {campaign_context.lead_info.organization_name}.
349
+ Do not spam or over-message.
350
+
351
+ =====================================================
352
+ NAMING AND STYLE
353
+ =====================================================
354
+
355
+ Use conversational company name
356
+ Use conversational lead first name
357
+ • Do not use special characters or spaces when referencing lead first name
358
+ Signature must include sender_first_name exactly as provided
359
+ • Preserve clean spacing and newlines
360
+ • Do NOT use em dash
361
+ • Keep it human. Avoid templates.
362
+
363
+ =====================================================
364
+ OUTPUT FORMAT (STRICT JSON)
365
+ =====================================================
366
+
367
+ Return valid JSON only.
368
+
369
+ {
361
370
  "triage_status": "AUTOMATIC" or "END_CONVERSATION",
362
- "triage_reason": "<reason if END_CONVERSATION; otherwise null>",
371
+ "triage_reason": "<string if END_CONVERSATION, otherwise null>",
363
372
  "response_action_to_take": "one of {allowed_actions}",
364
- "response_message": "<the reply body if response_action_to_take is SEND_REPLY or SCHEDULE_MEETING; otherwise empty>"
365
- }}
373
+ "response_message": "<reply body only if SEND_REPLY or SCHEDULE_MEETING, otherwise empty>"
374
+ }
375
+
376
+ =====================================================
377
+ SYSTEM CONTEXT
378
+ =====================================================
366
379
 
367
- Current date is: {current_date_iso}.
368
- -----------------------------------------------------------------
380
+ Current date: {current_date_iso}
381
+ • Use only provided context
382
+ • If unsure, choose END_CONVERSATION
369
383
  """
370
384
 
371
385
 
386
+
372
387
  # If there's a vector store ID, use that approach
373
388
  if (
374
389
  cleaned_context.external_known_data
@@ -1561,6 +1561,36 @@ async def test_dialpad(client_id: str, client_secret: str) -> Dict[str, Any]:
1561
1561
  return {"success": False, "status_code": 0, "error_message": str(exc)}
1562
1562
 
1563
1563
 
1564
+ async def test_twilio(account_sid: str, auth_token: str) -> Dict[str, Any]:
1565
+ """
1566
+ Validate Twilio credentials via a lightweight authenticated call.
1567
+ Uses HTTP Basic Auth (account_sid:auth_token) to fetch account info.
1568
+ """
1569
+ url = f"https://api.twilio.com/2010-04-01/Accounts/{account_sid}.json"
1570
+
1571
+ try:
1572
+ auth = aiohttp.BasicAuth(account_sid, auth_token)
1573
+ async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=10), auth=auth) as session:
1574
+ async with session.get(url) as response:
1575
+ status = response.status
1576
+ data = await safe_json(response)
1577
+
1578
+ if status == 200 and isinstance(data, dict) and "sid" in data:
1579
+ return {"success": True, "status_code": status, "error_message": None}
1580
+
1581
+ message = None
1582
+ if isinstance(data, dict):
1583
+ message = data.get("message") or data.get("error")
1584
+ return {
1585
+ "success": False,
1586
+ "status_code": status,
1587
+ "error_message": message or f"Twilio responded with {status}",
1588
+ }
1589
+ except Exception as exc:
1590
+ logger.error(f"Twilio connectivity test failed: {exc}")
1591
+ return {"success": False, "status_code": 0, "error_message": str(exc)}
1592
+
1593
+
1564
1594
  async def test_nooks(api_key: str) -> Dict[str, Any]:
1565
1595
  """
1566
1596
  Validate Nooks.ai API key via a simple authenticated call.
@@ -1880,6 +1910,7 @@ async def test_connectivity(tool_config: List[Dict[str, Any]]) -> Dict[str, Dict
1880
1910
  "aircall": test_aircall, # handled specially to pass appId + apiToken
1881
1911
  "ringover": test_ringover,
1882
1912
  "dialpad": test_dialpad, # handled specially to pass client credentials
1913
+ "twilio": test_twilio, # handled specially to pass account_sid + auth_token
1883
1914
  "nooks": test_nooks,
1884
1915
  "commonRoom": test_commonroom,
1885
1916
  "scarf": test_scarf,
@@ -2097,6 +2128,23 @@ async def test_connectivity(tool_config: List[Dict[str, Any]]) -> Dict[str, Dict
2097
2128
  results[tool_name] = await test_dialpad(client_id, client_secret)
2098
2129
  continue
2099
2130
 
2131
+ # ------------------------------------------------------------------ #
2132
+ # Special-case: Twilio (account_sid + auth_token)
2133
+ # ------------------------------------------------------------------ #
2134
+ if tool_name == "twilio":
2135
+ account_sid = next((c["value"] for c in config_entries if c["name"] in ("accountSid", "account_sid")), None)
2136
+ auth_token = next((c["value"] for c in config_entries if c["name"] in ("authToken", "auth_token")), None)
2137
+ if not account_sid or not auth_token:
2138
+ results[tool_name] = {
2139
+ "success": False,
2140
+ "status_code": 0,
2141
+ "error_message": "Missing accountSid or authToken for Twilio.",
2142
+ }
2143
+ else:
2144
+ logger.info("Testing connectivity for Twilio…")
2145
+ results[tool_name] = await test_twilio(account_sid, auth_token)
2146
+ continue
2147
+
2100
2148
  # ------------------------------------------------------------------ #
2101
2149
  # All other tools – expect an apiKey by default
2102
2150
  # ------------------------------------------------------------------ #
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dhisana
3
- Version: 0.0.1.dev254
3
+ Version: 0.0.1.dev256
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
File without changes
File without changes