dhisana 0.0.1.dev262__tar.gz → 0.0.1.dev264__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/PKG-INFO +1 -1
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/setup.py +1 -1
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/schemas/sales.py +3 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/enrich_lead_information.py +1 -1
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/generate_custom_message.py +1 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/generate_email.py +1 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/generate_email_response.py +45 -6
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/generate_leads_salesnav.py +2 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/generate_linkedin_connect_message.py +1 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/generate_linkedin_response_message.py +2 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/generate_structured_output_internal.py +38 -13
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana.egg-info/PKG-INFO +1 -1
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/README.md +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/pyproject.toml +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/setup.cfg +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/__init__.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/cli/__init__.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/cli/cli.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/cli/datasets.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/cli/models.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/cli/predictions.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/schemas/__init__.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/schemas/common.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/ui/__init__.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/ui/components.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/__init__.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/add_mapping.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/agent_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/apollo_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/assistant_tool_tag.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/built_with_api_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/cache_output_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/cache_output_tools_local.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/check_email_validity_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/check_for_intent_signal.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/check_linkedin_url_validity.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/clay_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/clean_properties.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/company_utils.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/compose_salesnav_query.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/compose_search_query.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/compose_three_step_workflow.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/composite_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/dataframe_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/domain_parser.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/email_body_utils.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/email_parse_helpers.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/email_provider.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/extract_email_content_for_llm.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/fetch_openai_config.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/field_validators.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/g2_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/generate_content.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/generate_flow.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/google_custom_search.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/google_oauth_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/google_workspace_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/hubspot_clearbit.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/hubspot_crm_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/instantly_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/linkedin_crawler.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/lusha_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/mailgun_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/mailreach_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/microsoft365_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/openai_assistant_and_file_utils.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/openai_helpers.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/openapi_spec_to_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/openapi_tool/__init__.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/openapi_tool/api_models.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/openapi_tool/convert_openai_spec_to_tool.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/openapi_tool/openapi_tool.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/parse_linkedin_messages_txt.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/profile.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/proxy_curl_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/proxycurl_search_leads.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/python_function_to_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/research_lead.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/sales_navigator_crawler.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/salesforce_crm_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/search_router.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/search_router_jobs.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/sendgrid_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/serarch_router_local_business.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/serpapi_additional_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/serpapi_google_jobs.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/serpapi_google_search.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/serpapi_local_business_search.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/serpapi_search_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/serperdev_google_jobs.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/serperdev_local_business.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/serperdev_search.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/smtp_email_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/test_connect.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/trasform_json.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/web_download_parse_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/workflow_code_model.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/zoominfo_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/workflow/__init__.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/workflow/agent.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/workflow/flow.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/workflow/task.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/workflow/test.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana.egg-info/SOURCES.txt +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana.egg-info/dependency_links.txt +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana.egg-info/entry_points.txt +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana.egg-info/requires.txt +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana.egg-info/top_level.txt +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/tests/test_agent_tools.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/tests/test_apollo_company_search.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/tests/test_apollo_lead_search.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/tests/test_connectivity.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/tests/test_email_body_utils.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/tests/test_google_document.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/tests/test_hubspot_call_logs.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/tests/test_linkedin_serper.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/tests/test_mailreach.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/tests/test_mcp_connectivity.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/tests/test_proxycurl_get_company_search_id.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/tests/test_proxycurl_job_count.py +0 -0
- {dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/tests/test_structured_output_with_mcp.py +0 -0
|
@@ -278,13 +278,16 @@ class SenderInfo(BaseModel):
|
|
|
278
278
|
- sender_full_name: Full name of the sender.
|
|
279
279
|
- sender_first_name: Sender's first name.
|
|
280
280
|
- sender_last_name: Sender's last name.
|
|
281
|
+
- sender_email: Sender's email address.
|
|
281
282
|
- sender_bio: Optional biography or short description of the sender.
|
|
283
|
+
- sender_appointment_booking_url: Optional URL for booking an appointment with the sender.
|
|
282
284
|
"""
|
|
283
285
|
sender_full_name: Optional[str] = None
|
|
284
286
|
sender_first_name: Optional[str] = None
|
|
285
287
|
sender_last_name: Optional[str] = None
|
|
286
288
|
sender_email: Optional[str] = None
|
|
287
289
|
sender_bio: Optional[str] = None
|
|
290
|
+
sender_appointment_booking_url: Optional[str] = None
|
|
288
291
|
|
|
289
292
|
|
|
290
293
|
class MessageGenerationInstructions(BaseModel):
|
|
@@ -147,6 +147,7 @@ async def generate_custom_message_copy(
|
|
|
147
147
|
First Name: {sender_data.sender_first_name or ''}
|
|
148
148
|
Last Name: {sender_data.sender_last_name or ''}
|
|
149
149
|
Bio: {sender_data.sender_bio or ''}
|
|
150
|
+
Appointment Booking URL: {sender_data.sender_appointment_booking_url or ''}
|
|
150
151
|
|
|
151
152
|
3) Campaign Information:
|
|
152
153
|
Product Name: {campaign_data.product_name or ''}
|
|
@@ -154,6 +154,7 @@ async def generate_personalized_email_copy(
|
|
|
154
154
|
First Name: {sender_data.sender_first_name or ''}
|
|
155
155
|
Last Name: {sender_data.sender_last_name or ''}
|
|
156
156
|
Bio: {sender_data.sender_bio or ''}
|
|
157
|
+
Appointment Booking URL: {sender_data.sender_appointment_booking_url or ''}
|
|
157
158
|
|
|
158
159
|
3) Campaign Information:
|
|
159
160
|
Product Name: {campaign_data.product_name or ''}
|
|
@@ -16,6 +16,11 @@ from dhisana.utils.generate_structured_output_internal import (
|
|
|
16
16
|
get_structured_output_internal
|
|
17
17
|
)
|
|
18
18
|
|
|
19
|
+
# ---------------------------------------------------------------------------------------
|
|
20
|
+
# CONSTANTS
|
|
21
|
+
# ---------------------------------------------------------------------------------------
|
|
22
|
+
DEFAULT_TRIAGE_MODEL = "gpt-4.1"
|
|
23
|
+
|
|
19
24
|
# ---------------------------------------------------------------------------------------
|
|
20
25
|
# MODEL
|
|
21
26
|
# ---------------------------------------------------------------------------------------
|
|
@@ -194,7 +199,7 @@ async def get_inbound_email_triage_action(
|
|
|
194
199
|
triage_only, status = await get_structured_output_with_assistant_and_vector_store(
|
|
195
200
|
prompt=triage_prompt,
|
|
196
201
|
response_format=InboundEmailTriageResponse,
|
|
197
|
-
model=
|
|
202
|
+
model=DEFAULT_TRIAGE_MODEL,
|
|
198
203
|
vector_store_id=cleaned_context.external_known_data.external_openai_vector_store_id,
|
|
199
204
|
tool_config=tool_config,
|
|
200
205
|
use_cache=cleaned_context.message_instructions.use_cache if cleaned_context.message_instructions else True
|
|
@@ -203,13 +208,28 @@ async def get_inbound_email_triage_action(
|
|
|
203
208
|
triage_only, status = await get_structured_output_internal(
|
|
204
209
|
prompt=triage_prompt,
|
|
205
210
|
response_format=InboundEmailTriageResponse,
|
|
206
|
-
model=
|
|
211
|
+
model=DEFAULT_TRIAGE_MODEL,
|
|
207
212
|
tool_config=tool_config,
|
|
208
213
|
use_cache=cleaned_context.message_instructions.use_cache if cleaned_context.message_instructions else True
|
|
209
214
|
)
|
|
210
215
|
|
|
211
216
|
if status != "SUCCESS":
|
|
212
|
-
|
|
217
|
+
campaign_id = context.campaign_context.campaign_id if context.campaign_context else 'N/A'
|
|
218
|
+
if status == "CONTEXT_LENGTH_EXCEEDED":
|
|
219
|
+
raise Exception(
|
|
220
|
+
f"Email thread too long for model. Campaign ID: {campaign_id}. "
|
|
221
|
+
f"Consider truncating thread or switching models."
|
|
222
|
+
)
|
|
223
|
+
elif status in ("API_ERROR", "ERROR"):
|
|
224
|
+
raise Exception(
|
|
225
|
+
f"Error in generating triage action. Status: {status}. "
|
|
226
|
+
f"Campaign ID: {campaign_id}. Details: {triage_only}"
|
|
227
|
+
)
|
|
228
|
+
else:
|
|
229
|
+
raise Exception(
|
|
230
|
+
f"Error in generating triage action. Status: {status}. "
|
|
231
|
+
f"Campaign ID: {campaign_id}"
|
|
232
|
+
)
|
|
213
233
|
return triage_only
|
|
214
234
|
|
|
215
235
|
|
|
@@ -269,6 +289,7 @@ async def generate_inbound_email_response_copy(
|
|
|
269
289
|
- First name: {sender_data.sender_first_name or ''}
|
|
270
290
|
- Last name: {sender_data.sender_last_name or ''}
|
|
271
291
|
- Bio: {sender_data.sender_bio or ''}
|
|
292
|
+
- Appointment Booking URL: {sender_data.sender_appointment_booking_url or ''}
|
|
272
293
|
|
|
273
294
|
3) Campaign-specific triage guidelines
|
|
274
295
|
(User overrides always take priority):
|
|
@@ -392,7 +413,7 @@ async def generate_inbound_email_response_copy(
|
|
|
392
413
|
initial_response, status = await get_structured_output_with_assistant_and_vector_store(
|
|
393
414
|
prompt=prompt,
|
|
394
415
|
response_format=InboundEmailTriageResponse,
|
|
395
|
-
model=
|
|
416
|
+
model=DEFAULT_TRIAGE_MODEL,
|
|
396
417
|
vector_store_id=cleaned_context.external_known_data.external_openai_vector_store_id,
|
|
397
418
|
tool_config=tool_config
|
|
398
419
|
)
|
|
@@ -400,12 +421,30 @@ async def generate_inbound_email_response_copy(
|
|
|
400
421
|
initial_response, status = await get_structured_output_internal(
|
|
401
422
|
prompt=prompt,
|
|
402
423
|
response_format=InboundEmailTriageResponse,
|
|
403
|
-
model=
|
|
424
|
+
model=DEFAULT_TRIAGE_MODEL,
|
|
404
425
|
tool_config=tool_config
|
|
405
426
|
)
|
|
406
427
|
|
|
407
428
|
if status != "SUCCESS":
|
|
408
|
-
|
|
429
|
+
campaign_id = (
|
|
430
|
+
campaign_context.campaign_context.campaign_id
|
|
431
|
+
if campaign_context.campaign_context else 'N/A'
|
|
432
|
+
)
|
|
433
|
+
if status == "CONTEXT_LENGTH_EXCEEDED":
|
|
434
|
+
raise Exception(
|
|
435
|
+
f"Email thread too long for model. Campaign ID: {campaign_id}. "
|
|
436
|
+
f"Consider truncating thread or switching models."
|
|
437
|
+
)
|
|
438
|
+
elif status in ("API_ERROR", "ERROR"):
|
|
439
|
+
raise Exception(
|
|
440
|
+
f"Error in generating inbound email triage response. Status: {status}. "
|
|
441
|
+
f"Campaign ID: {campaign_id}. Details: {initial_response}"
|
|
442
|
+
)
|
|
443
|
+
else:
|
|
444
|
+
raise Exception(
|
|
445
|
+
f"Error in generating inbound email triage response. Status: {status}. "
|
|
446
|
+
f"Campaign ID: {campaign_id}"
|
|
447
|
+
)
|
|
409
448
|
|
|
410
449
|
response_item = MessageItem(
|
|
411
450
|
message_id="", # or generate one if appropriate
|
{dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/generate_linkedin_connect_message.py
RENAMED
|
@@ -105,6 +105,7 @@ async def generate_personalized_linkedin_copy(
|
|
|
105
105
|
First Name: {sender_data.sender_first_name or ''}
|
|
106
106
|
Last Name: {sender_data.sender_last_name or ''}
|
|
107
107
|
Bio: {sender_data.sender_bio or ''}
|
|
108
|
+
Appointment Booking URL: {sender_data.sender_appointment_booking_url or ''}
|
|
108
109
|
|
|
109
110
|
3) Campaign Information:
|
|
110
111
|
Product Name: {campaign_data.product_name or ''}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from typing import Any, Dict, List, Optional
|
|
2
2
|
from pydantic import BaseModel
|
|
3
3
|
from dhisana.schemas.sales import (
|
|
4
|
+
CampaignContext,
|
|
4
5
|
ContentGenerationContext,
|
|
5
6
|
Lead,
|
|
6
7
|
MessageItem,
|
|
@@ -118,6 +119,7 @@ async def generate_linkedin_response_message_copy(
|
|
|
118
119
|
First Name: {sender_data.sender_first_name or ''}
|
|
119
120
|
Last Name: {sender_data.sender_last_name or ''}
|
|
120
121
|
Bio: {sender_data.sender_bio or ''}
|
|
122
|
+
Appointment Booking URL: {sender_data.sender_appointment_booking_url or ''}
|
|
121
123
|
|
|
122
124
|
3. Campaign-specific triage guidelines (user overrides always win):
|
|
123
125
|
{campaign_context.linkedin_triage_guidelines}
|
|
@@ -3,6 +3,7 @@ import hashlib
|
|
|
3
3
|
import json
|
|
4
4
|
import logging
|
|
5
5
|
import random
|
|
6
|
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
6
7
|
|
|
7
8
|
from fastapi import HTTPException
|
|
8
9
|
from pydantic import BaseModel
|
|
@@ -17,11 +18,16 @@ from dhisana.utils.fetch_openai_config import (
|
|
|
17
18
|
_extract_config,
|
|
18
19
|
create_async_openai_client,
|
|
19
20
|
)
|
|
20
|
-
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
21
21
|
|
|
22
|
-
from openai import OpenAIError, RateLimitError
|
|
23
|
-
from pydantic import BaseModel
|
|
24
22
|
|
|
23
|
+
# ──────────────────────────────────────────────────────────────────────────────
|
|
24
|
+
# 1. Helper functions
|
|
25
|
+
# ──────────────────────────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
def is_context_length_error(error: Exception) -> bool:
|
|
28
|
+
"""Check if an error is due to context length being exceeded."""
|
|
29
|
+
error_str = str(error).lower()
|
|
30
|
+
return "context_length_exceeded" in error_str or "context window" in error_str
|
|
25
31
|
|
|
26
32
|
|
|
27
33
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
@@ -144,11 +150,17 @@ async def get_structured_output_internal(
|
|
|
144
150
|
completion = await _make_request()
|
|
145
151
|
break # success → exit loop
|
|
146
152
|
except (RateLimitError, OpenAIError) as e:
|
|
153
|
+
# Check for context length exceeded error
|
|
154
|
+
if is_context_length_error(e):
|
|
155
|
+
logging.error(f"Context length exceeded: {e}")
|
|
156
|
+
return f"Context length exceeded: {str(e)}", "CONTEXT_LENGTH_EXCEEDED"
|
|
157
|
+
|
|
147
158
|
# Detect 429 / rate-limit
|
|
159
|
+
error_str = str(e).lower()
|
|
148
160
|
is_rl = (
|
|
149
161
|
isinstance(e, RateLimitError)
|
|
150
162
|
or getattr(e, "status_code", None) == 429
|
|
151
|
-
or "rate_limit" in
|
|
163
|
+
or "rate_limit" in error_str
|
|
152
164
|
)
|
|
153
165
|
if is_rl and attempt < max_retries:
|
|
154
166
|
attempt += 1
|
|
@@ -161,7 +173,7 @@ async def get_structured_output_internal(
|
|
|
161
173
|
await asyncio.sleep(wait_time)
|
|
162
174
|
continue # retry once
|
|
163
175
|
logging.error(f"OpenAI API error: {e}")
|
|
164
|
-
|
|
176
|
+
return f"OpenAI API error: {str(e)}", "API_ERROR"
|
|
165
177
|
|
|
166
178
|
# ─── handle model output (unchanged) ────────────────────────────────
|
|
167
179
|
if completion and completion.output and len(completion.output) > 0:
|
|
@@ -203,12 +215,15 @@ async def get_structured_output_internal(
|
|
|
203
215
|
else:
|
|
204
216
|
return "No output returned", "FAIL"
|
|
205
217
|
|
|
218
|
+
# Safety fallback: catch any OpenAI errors not caught by inner retry loop
|
|
206
219
|
except OpenAIError as e:
|
|
207
220
|
logging.error(f"OpenAI API error: {e}")
|
|
208
|
-
|
|
221
|
+
if is_context_length_error(e):
|
|
222
|
+
return f"Context length exceeded: {str(e)}", "CONTEXT_LENGTH_EXCEEDED"
|
|
223
|
+
return f"OpenAI API error: {str(e)}", "API_ERROR"
|
|
209
224
|
except Exception as e:
|
|
210
225
|
logging.error(f"Unexpected error: {e}")
|
|
211
|
-
|
|
226
|
+
return f"Unexpected error: {str(e)}", "ERROR"
|
|
212
227
|
|
|
213
228
|
|
|
214
229
|
|
|
@@ -293,11 +308,17 @@ async def get_structured_output_with_mcp(
|
|
|
293
308
|
return await client_async.responses.create(**kwargs)
|
|
294
309
|
|
|
295
310
|
# ─── Retry once for 429s ──────────────────────────────────────────────────
|
|
311
|
+
completion = None
|
|
296
312
|
for attempt in range(2):
|
|
297
313
|
try:
|
|
298
314
|
completion = await _make_request()
|
|
299
315
|
break
|
|
300
316
|
except (RateLimitError, OpenAIError) as exc:
|
|
317
|
+
# Check for context length exceeded error
|
|
318
|
+
if is_context_length_error(exc):
|
|
319
|
+
logging.error(f"Context length exceeded: {exc}")
|
|
320
|
+
return f"Context length exceeded: {str(exc)}", "CONTEXT_LENGTH_EXCEEDED"
|
|
321
|
+
|
|
301
322
|
if attempt == 0 and (
|
|
302
323
|
isinstance(exc, RateLimitError)
|
|
303
324
|
or getattr(exc, "status_code", None) == 429
|
|
@@ -308,9 +329,10 @@ async def get_structured_output_with_mcp(
|
|
|
308
329
|
await asyncio.sleep(sleep_for)
|
|
309
330
|
continue
|
|
310
331
|
logging.error("OpenAI API error: %s", exc)
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
332
|
+
return f"OpenAI API error: {str(exc)}", "API_ERROR"
|
|
333
|
+
|
|
334
|
+
if not completion:
|
|
335
|
+
return "OpenAI request retry loop failed", "API_ERROR"
|
|
314
336
|
|
|
315
337
|
# ─── Parse the model’s structured output ──────────────────────────────────
|
|
316
338
|
if not (completion and completion.output):
|
|
@@ -438,7 +460,7 @@ async def get_structured_output_with_assistant_and_vector_store(
|
|
|
438
460
|
break
|
|
439
461
|
|
|
440
462
|
if not raw_text or not raw_text.strip():
|
|
441
|
-
|
|
463
|
+
return "No response from the model", "FAIL"
|
|
442
464
|
|
|
443
465
|
try:
|
|
444
466
|
parsed_obj = response_format.parse_raw(raw_text)
|
|
@@ -454,9 +476,12 @@ async def get_structured_output_with_assistant_and_vector_store(
|
|
|
454
476
|
else:
|
|
455
477
|
return "No output returned", "FAIL"
|
|
456
478
|
|
|
479
|
+
# Safety fallback: catch any errors not caught during API call
|
|
457
480
|
except OpenAIError as e:
|
|
458
481
|
logging.error(f"OpenAI API error: {e}")
|
|
459
|
-
|
|
482
|
+
if is_context_length_error(e):
|
|
483
|
+
return f"Context length exceeded: {str(e)}", "CONTEXT_LENGTH_EXCEEDED"
|
|
484
|
+
return f"OpenAI API error: {str(e)}", "API_ERROR"
|
|
460
485
|
except Exception as e:
|
|
461
486
|
logging.error(f"Unexpected error: {e}")
|
|
462
|
-
|
|
487
|
+
return f"Unexpected error: {str(e)}", "ERROR"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/check_email_validity_tools.py
RENAMED
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/check_linkedin_url_validity.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/compose_three_step_workflow.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/extract_email_content_for_llm.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/openai_assistant_and_file_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/openapi_tool/openapi_tool.py
RENAMED
|
File without changes
|
{dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/parse_linkedin_messages_txt.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/serarch_router_local_business.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev262 → dhisana-0.0.1.dev264}/src/dhisana/utils/serpapi_local_business_search.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|