dhisana 0.0.1.dev101__tar.gz → 0.0.1.dev103__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 (88) hide show
  1. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/PKG-INFO +1 -1
  2. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/setup.py +1 -1
  3. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/apollo_tools.py +11 -1
  4. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/check_email_validity_tools.py +17 -1
  5. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/enrich_lead_information.py +3 -3
  6. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/generate_email.py +1 -0
  7. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/generate_email_response.py +4 -0
  8. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/generate_linkedin_connect_message.py +4 -4
  9. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/generate_linkedin_response_message.py +19 -4
  10. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/openai_assistant_and_file_utils.py +3 -3
  11. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/research_lead.py +33 -8
  12. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/test_connect.py +1 -1
  13. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana.egg-info/PKG-INFO +1 -1
  14. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/README.md +0 -0
  15. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/pyproject.toml +0 -0
  16. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/setup.cfg +0 -0
  17. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/__init__.py +0 -0
  18. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/cli/__init__.py +0 -0
  19. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/cli/cli.py +0 -0
  20. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/cli/datasets.py +0 -0
  21. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/cli/models.py +0 -0
  22. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/cli/predictions.py +0 -0
  23. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/schemas/__init__.py +0 -0
  24. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/schemas/common.py +0 -0
  25. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/schemas/sales.py +0 -0
  26. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/ui/__init__.py +0 -0
  27. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/ui/components.py +0 -0
  28. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/__init__.py +0 -0
  29. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/add_mapping.py +0 -0
  30. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/agent_tools.py +0 -0
  31. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/assistant_tool_tag.py +0 -0
  32. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/built_with_api_tools.py +0 -0
  33. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/cache_output_tools.py +0 -0
  34. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/cache_output_tools_local.py +0 -0
  35. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/check_for_intent_signal.py +0 -0
  36. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/check_linkedin_url_validity.py +0 -0
  37. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/clay_tools.py +0 -0
  38. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/clean_properties.py +0 -0
  39. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/company_utils.py +0 -0
  40. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/compose_salesnav_query.py +0 -0
  41. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/compose_search_query.py +0 -0
  42. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/compose_three_step_workflow.py +0 -0
  43. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/composite_tools.py +0 -0
  44. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/dataframe_tools.py +0 -0
  45. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/domain_parser.py +0 -0
  46. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/email_parse_helpers.py +0 -0
  47. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/extract_email_content_for_llm.py +0 -0
  48. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/field_validators.py +0 -0
  49. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/g2_tools.py +0 -0
  50. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/generate_content.py +0 -0
  51. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/generate_flow.py +0 -0
  52. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/generate_leads_salesnav.py +0 -0
  53. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/generate_structured_output_internal.py +0 -0
  54. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/google_custom_search.py +0 -0
  55. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/google_workspace_tools.py +0 -0
  56. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/hubspot_clearbit.py +0 -0
  57. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/hubspot_crm_tools.py +0 -0
  58. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/instantly_tools.py +0 -0
  59. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/linkedin_crawler.py +0 -0
  60. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/lusha_tools.py +0 -0
  61. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/openai_helpers.py +0 -0
  62. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/openapi_spec_to_tools.py +0 -0
  63. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/openapi_tool/__init__.py +0 -0
  64. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/openapi_tool/api_models.py +0 -0
  65. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/openapi_tool/convert_openai_spec_to_tool.py +0 -0
  66. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/openapi_tool/openapi_tool.py +0 -0
  67. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/parse_linkedin_messages_txt.py +0 -0
  68. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/proxy_curl_tools.py +0 -0
  69. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/python_function_to_tools.py +0 -0
  70. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/sales_navigator_crawler.py +0 -0
  71. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/salesforce_crm_tools.py +0 -0
  72. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/sendgrid_tools.py +0 -0
  73. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/serpapi_search_tools.py +0 -0
  74. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/trasform_json.py +0 -0
  75. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/web_download_parse_tools.py +0 -0
  76. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/workflow_code_model.py +0 -0
  77. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/utils/zoominfo_tools.py +0 -0
  78. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/workflow/__init__.py +0 -0
  79. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/workflow/agent.py +0 -0
  80. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/workflow/flow.py +0 -0
  81. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/workflow/task.py +0 -0
  82. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana/workflow/test.py +0 -0
  83. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana.egg-info/SOURCES.txt +0 -0
  84. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana.egg-info/dependency_links.txt +0 -0
  85. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana.egg-info/entry_points.txt +0 -0
  86. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana.egg-info/requires.txt +0 -0
  87. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/src/dhisana.egg-info/top_level.txt +0 -0
  88. {dhisana-0.0.1.dev101 → dhisana-0.0.1.dev103}/tests/test_agent_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dhisana
3
- Version: 0.0.1.dev101
3
+ Version: 0.0.1.dev103
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-dev101',
5
+ version='0.0.1-dev103',
6
6
  description='A Python SDK for Dhisana AI Platform',
7
7
  author='Admin',
8
8
  author_email='contact@dhisana.ai',
@@ -192,6 +192,16 @@ async def lookup_person_in_apollo_by_name(
192
192
  "per_page": 10
193
193
  }
194
194
 
195
+ # Build a cache key that includes full_name and company_name (if provided)
196
+ # so that results are correctly cached and retrieved.
197
+ key_item = f"lookup_person_in_apollo_by_name_{full_name}_{company_name or ''}".lower()
198
+
199
+ # Attempt to retrieve a cached response first
200
+ cached_response = retrieve_output("lookup_person_in_apollo_by_name", key_item)
201
+ if cached_response is not None:
202
+ logger.info(f"Cache hit for user: {full_name}, company: {company_name or ''}")
203
+ return cached_response
204
+
195
205
  url = 'https://api.apollo.io/api/v1/mixed_people/search'
196
206
  logger.debug(f"Making request to Apollo with payload: {data}")
197
207
 
@@ -202,6 +212,7 @@ async def lookup_person_in_apollo_by_name(
202
212
  if response.status == 200:
203
213
  result = await response.json()
204
214
  logger.info("Successfully looked up person by name on Apollo.")
215
+ cache_output("lookup_person_in_apollo_by_name", key_item, result)
205
216
  return result
206
217
  elif response.status == 429:
207
218
  msg = "Rate limit exceeded"
@@ -222,7 +233,6 @@ async def lookup_person_in_apollo_by_name(
222
233
  logger.exception("Exception occurred while looking up person by name.")
223
234
  return {'error': str(e)}
224
235
 
225
-
226
236
  @assistant_tool
227
237
  @backoff.on_exception(
228
238
  backoff.expo,
@@ -220,6 +220,18 @@ async def check_email_validity_with_hunter(
220
220
  "confidence": "low",
221
221
  "is_valid": False
222
222
  }
223
+
224
+ cache_key = f"{email_id}"
225
+ cached_response = retrieve_output("hunter_validate", cache_key)
226
+ if cached_response is not None:
227
+ logger.info("Cache hit for Hunter validate.")
228
+ if not cached_response:
229
+ return {
230
+ "email": email_id,
231
+ "confidence": "low",
232
+ "is_valid": False
233
+ }
234
+ return json.loads(cached_response[0])
223
235
 
224
236
  HUNTER_API_KEY = get_hunter_access_token(tool_config)
225
237
  if not HUNTER_API_KEY:
@@ -241,11 +253,14 @@ async def check_email_validity_with_hunter(
241
253
  if response.status != 200:
242
254
  content = await safe_read_json_or_text(response)
243
255
  logger.warning(f"[Hunter] Non-200 status: {response.status} => {content}")
244
- return {
256
+
257
+ final_response = {
245
258
  "email": email_id,
246
259
  "confidence": "low",
247
260
  "is_valid": False
248
261
  }
262
+ cache_output("hunter_validate", cache_key, [json.dumps(final_response)])
263
+ return final_response
249
264
 
250
265
  result = await response.json()
251
266
  except Exception as ex:
@@ -266,6 +281,7 @@ async def check_email_validity_with_hunter(
266
281
  "confidence": confidence,
267
282
  "is_valid": is_valid
268
283
  }
284
+ cache_output("hunter_validate", cache_key, [json.dumps(final_response)])
269
285
  logger.info("Exiting check_email_validity_with_hunter.")
270
286
  return final_response
271
287
 
@@ -162,7 +162,7 @@ async def get_clean_lead_info_with_llm(lead_info_str: str, tool_config: Optional
162
162
  lead_info, status = await get_structured_output_internal(
163
163
  prompt,
164
164
  BasicLeadInformation,
165
- model="gpt-4o",
165
+ model="gpt-4.1",
166
166
  tool_config=tool_config
167
167
  )
168
168
  if status == "ERROR":
@@ -411,7 +411,7 @@ async def get_user_linkedin_url_from_github_profile(
411
411
  response, status = await get_structured_output_internal(
412
412
  instructions,
413
413
  UserInfoFromGithubProfileId,
414
- model="gpt-4o",
414
+ model="gpt-4.1",
415
415
  use_web_search=True,
416
416
  tool_config=tool_config
417
417
  )
@@ -783,7 +783,7 @@ async def get_company_domain_from_llm_web_search(
783
783
  response, status = await get_structured_output_internal(
784
784
  instructions,
785
785
  CompanyInfoFromName,
786
- model="gpt-4o",
786
+ model="gpt-4.1",
787
787
  use_web_search=True,
788
788
  tool_config=tool_config
789
789
  )
@@ -133,6 +133,7 @@ async def generate_personalized_email_copy(
133
133
  - Do not include PII or internal references, guids or content identifiers in the email.
134
134
  - User conversational name for company name if used.
135
135
  - Email has saluation Hi <First Name>, unless otherwise specified.
136
+ - <First Name> is the first name of the lead. Its conversational name. It does not have any special characters or spaces.
136
137
  """
137
138
 
138
139
  # Check if a vector store is available
@@ -106,6 +106,10 @@ async def generate_inbound_email_response_copy(
106
106
  DO NOT share any link to internal or made up doucment. You can attach or send any document.
107
107
  If the user is asking for any document point them to organization's website found in sender information if available:
108
108
  {campaign_context.sender_info.model_dump()}
109
+
110
+ Use conversational name for company name.
111
+ Use conversational name when using lead first name.
112
+ Do not use special characters or spaces when use leads first name.
109
113
 
110
114
  Your final output must be valid JSON with the structure:
111
115
  {{
@@ -25,7 +25,6 @@ from dhisana.utils.assistant_tool_tag import assistant_tool
25
25
  # LinkedIn Connection Message Schema
26
26
  # ----------------------------------------------------------------------
27
27
  class LinkedInConnectMessage(BaseModel):
28
- subject: str
29
28
  body: str
30
29
 
31
30
  # ----------------------------------------------------------------------
@@ -125,11 +124,12 @@ async def generate_personalized_linkedin_copy(
125
124
  LinkedIn Thread: {conversation_data.current_linkedin_thread or ''}
126
125
 
127
126
  IMPORTANT REQUIREMENTS:
128
- - Output must be JSON with "subject" and "body" fields only.
129
- - The entire message must be under 40 words total (subject + body).
127
+ - Output must be JSON "body" of the message.
128
+ - The entire message body must be under 40 words total.
130
129
  - No placeholders or extra instructions in the final output.
131
130
  - Do not include personal addresses, IDs, or irrelevant internal data.
132
131
  - Linked in message always has saluatation Hi <First Name>, unless specified otherwise.
132
+ - <First Name> is the first name of the lead. Its conversational name. It does not have any special characters or spaces.
133
133
  - User conversational name for company name if used.
134
134
  """
135
135
 
@@ -166,7 +166,7 @@ async def generate_personalized_linkedin_copy(
166
166
  receiver_name=lead_data.full_name or "",
167
167
  receiver_email=lead_data.email or "",
168
168
  iso_datetime=datetime.utcnow().isoformat(),
169
- subject=response_data.subject,
169
+ subject="Hi",
170
170
  body=response_data.body
171
171
  )
172
172
 
@@ -69,9 +69,24 @@ async def generate_linkedin_response_message_copy(
69
69
  ]
70
70
 
71
71
  cleaned_context = cleanup_reply_linkedin_context(linkedin_context)
72
- if not cleaned_context.current_conversation_context.current_email_thread:
73
- cleaned_context.current_conversation_context.current_email_thread = []
74
-
72
+
73
+ # Safely handle the current_conversation_context if it exists.
74
+ if cleaned_context.current_conversation_context:
75
+ if not cleaned_context.current_conversation_context.current_email_thread:
76
+ cleaned_context.current_conversation_context.current_email_thread = []
77
+
78
+ if not cleaned_context.current_conversation_context.current_linkedin_thread:
79
+ cleaned_context.current_conversation_context.current_linkedin_thread = []
80
+
81
+ # Safely extract the conversation thread for prompt formatting.
82
+ conversation_thread_dump = [
83
+ thread_item.model_dump()
84
+ for thread_item in cleaned_context.current_conversation_context.current_linkedin_thread
85
+ ]
86
+ else:
87
+ # If current_conversation_context is None, use an empty thread.
88
+ conversation_thread_dump = []
89
+
75
90
  current_date_iso = datetime.datetime.now().isoformat()
76
91
  prompt = f"""
77
92
  You are a specialized LinkedIn assistant.
@@ -89,7 +104,7 @@ async def generate_linkedin_response_message_copy(
89
104
  {variation}
90
105
 
91
106
  1. Understand the conversation thread:
92
- {[thread_item.model_dump() for thread_item in cleaned_context.current_conversation_context.current_linkedin_thread] if cleaned_context.current_conversation_context.current_linkedin_thread else []}
107
+ {conversation_thread_dump}
93
108
 
94
109
  2. User & Company (Lead) Info:
95
110
  {cleaned_context.model_dump()}
@@ -190,7 +190,7 @@ async def delete_files(
190
190
  async def run_file_search(
191
191
  query: str,
192
192
  vector_store_id: str,
193
- model: str = "gpt-4o",
193
+ model: str = "gpt-4.1",
194
194
  max_num_results: int = 5,
195
195
  store: bool = True,
196
196
  tool_config: Optional[List[Dict]] = None
@@ -260,7 +260,7 @@ async def run_file_search(
260
260
 
261
261
  async def run_response_text(
262
262
  prompt: str,
263
- model: str = "gpt-4o",
263
+ model: str = "gpt-4.1",
264
264
  max_tokens: int = 2048,
265
265
  store: bool = True,
266
266
  tool_config: Optional[List[Dict]] = None
@@ -288,7 +288,7 @@ async def run_response_text(
288
288
  async def run_response_structured(
289
289
  prompt: str,
290
290
  response_format: dict,
291
- model: str = "gpt-4o",
291
+ model: str = "gpt-4.1",
292
292
  max_tokens: int = 1024,
293
293
  store: bool = True,
294
294
  tool_config: Optional[List[Dict]] = None
@@ -8,6 +8,23 @@ def clean_nul_bytes(s: str) -> str:
8
8
  s = s.replace('```markdown', '')
9
9
  return s.replace('\x00', '')
10
10
 
11
+ def _remove_excluded_fields(data: Dict) -> Dict:
12
+ """
13
+ Return a copy of `data` that excludes keys named 'id'
14
+ or that end in '_by', '_id', '_to', or '_at'.
15
+ """
16
+ excluded_keys = {"id"}
17
+ excluded_endings = ["_by", "_id", "_to", "_at", "_status", "research_summary"]
18
+
19
+ cleaned = {}
20
+ for k, v in data.items():
21
+ if k in excluded_keys:
22
+ continue
23
+ if any(k.endswith(suffix) for suffix in excluded_endings):
24
+ continue
25
+ cleaned[k] = v
26
+ return cleaned
27
+
11
28
  class LeadResearchInformation(BaseModel):
12
29
  research_summary: str
13
30
 
@@ -20,7 +37,13 @@ async def research_lead_with_full_info_ai(
20
37
  """
21
38
  Research on lead provided given input. Provide Detailed Summary.
22
39
  """
40
+ # Clean user properties (e.g. remove newlines, sanitize strings, etc.)
23
41
  user_properties = cleanup_email_context(user_properties)
42
+
43
+ # Remove excluded fields from user_properties
44
+ user_properties = _remove_excluded_fields(user_properties)
45
+
46
+ # Optionally remove any known keys that should not appear (e.g. 'date_extracted')
24
47
  user_properties.pop("date_extracted", None)
25
48
 
26
49
  instructions = f"""
@@ -62,14 +85,14 @@ async def research_lead_with_full_info_ai(
62
85
  response, status = await get_structured_output_internal(
63
86
  instructions,
64
87
  LeadResearchInformation,
65
- model="gpt-4o",
88
+ model="gpt-4.1",
66
89
  tool_config=tool_config
67
90
  )
68
91
  if status == "SUCCESS":
69
92
  response.research_summary = clean_nul_bytes(response.research_summary)
70
93
  return response.model_dump()
71
94
  else:
72
- {"research_summary", ""}
95
+ return {"research_summary": ""}
73
96
 
74
97
  # --------------------------------------------
75
98
  # COMPANY-RELATED MODELS & FUNCTION (FIXED)
@@ -94,9 +117,12 @@ async def research_company_with_full_info_ai(
94
117
  Returns:
95
118
  dict: The JSON response containing the detailed research summary of the company.
96
119
  """
120
+ # Clean company properties (e.g. remove newlines, sanitize strings, etc.)
97
121
  company_properties = cleanup_email_context(company_properties)
98
122
 
99
- # Build the LLM instructions for the company research
123
+ # Remove excluded fields from company_properties
124
+ company_properties = _remove_excluded_fields(company_properties)
125
+
100
126
  instructions = f"""
101
127
  Please read the following company information and instructions, then produce a detailed summary of the company in the specified format.
102
128
  ---
@@ -130,11 +156,10 @@ async def research_company_with_full_info_ai(
130
156
  "research_summary": "Detailed summary about the company. The summary should be neatly formatted in GitHub-Flavored Markdown, and include all the key information from the listed sections."
131
157
  }}
132
158
  """
133
-
134
159
  response, status = await get_structured_output_internal(
135
- instructions,
136
- CompanyResearchInformation,
137
- model="gpt-4o",
160
+ instructions,
161
+ CompanyResearchInformation,
162
+ model="gpt-4.1",
138
163
  use_web_search=True,
139
164
  tool_config=tool_config
140
165
  )
@@ -142,4 +167,4 @@ async def research_company_with_full_info_ai(
142
167
  response.research_summary = clean_nul_bytes(response.research_summary)
143
168
  return response.model_dump()
144
169
  else:
145
- return {"research_summary", ""}
170
+ return {"research_summary": ""}
@@ -320,7 +320,7 @@ async def test_connectivity(tool_config: List[Dict[str, Any]]) -> Dict[str, Dict
320
320
 
321
321
  # Special handling for openai
322
322
  if tool_name == "openai":
323
- model_name = next((c["value"] for c in config_entries if c["name"] == "modelName"), "gpt-4o")
323
+ model_name = next((c["value"] for c in config_entries if c["name"] == "modelName"), "gpt-4.1")
324
324
  reasoning_effort = next((c["value"] for c in config_entries if c["name"] == "reasoningEffort"), "medium")
325
325
  results[tool_name] = await test_openai(api_key, model_name, reasoning_effort)
326
326
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dhisana
3
- Version: 0.0.1.dev101
3
+ Version: 0.0.1.dev103
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