dhisana 0.0.1.dev222__py3-none-any.whl → 0.0.1.dev224__py3-none-any.whl
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/utils/apollo_tools.py +2 -43
- dhisana/utils/test_connect.py +104 -0
- {dhisana-0.0.1.dev222.dist-info → dhisana-0.0.1.dev224.dist-info}/METADATA +1 -1
- {dhisana-0.0.1.dev222.dist-info → dhisana-0.0.1.dev224.dist-info}/RECORD +7 -7
- {dhisana-0.0.1.dev222.dist-info → dhisana-0.0.1.dev224.dist-info}/WHEEL +0 -0
- {dhisana-0.0.1.dev222.dist-info → dhisana-0.0.1.dev224.dist-info}/entry_points.txt +0 -0
- {dhisana-0.0.1.dev222.dist-info → dhisana-0.0.1.dev224.dist-info}/top_level.txt +0 -0
dhisana/utils/apollo_tools.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import hashlib
|
|
3
2
|
import json
|
|
4
3
|
import logging
|
|
5
4
|
import os
|
|
@@ -8,7 +7,6 @@ import aiohttp
|
|
|
8
7
|
import backoff
|
|
9
8
|
|
|
10
9
|
from dhisana.schemas.sales import LeadsQueryFilters, CompanyQueryFilters
|
|
11
|
-
from dhisana.utils.cache_output_tools import cache_output, retrieve_output
|
|
12
10
|
from dhisana.utils.assistant_tool_tag import assistant_tool
|
|
13
11
|
from urllib.parse import urlparse, parse_qs
|
|
14
12
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
@@ -141,10 +139,6 @@ async def enrich_person_info_from_apollo(
|
|
|
141
139
|
if linkedin_url:
|
|
142
140
|
logger.debug(f"LinkedIn URL provided: {linkedin_url}")
|
|
143
141
|
data['linkedin_url'] = linkedin_url
|
|
144
|
-
cached_response = retrieve_output("enrich_person_info_from_apollo", linkedin_url)
|
|
145
|
-
if cached_response is not None:
|
|
146
|
-
logger.info(f"Cache hit for LinkedIn URL: {linkedin_url}")
|
|
147
|
-
return cached_response
|
|
148
142
|
if email:
|
|
149
143
|
logger.debug(f"Email provided: {email}")
|
|
150
144
|
data['email'] = email
|
|
@@ -165,8 +159,6 @@ async def enrich_person_info_from_apollo(
|
|
|
165
159
|
logger.debug(f"Received response status: {response.status}")
|
|
166
160
|
if response.status == 200:
|
|
167
161
|
result = await response.json()
|
|
168
|
-
if linkedin_url:
|
|
169
|
-
cache_output("enrich_person_info_from_apollo", linkedin_url, result)
|
|
170
162
|
logger.info("Successfully retrieved person info from Apollo.")
|
|
171
163
|
return result
|
|
172
164
|
elif response.status == 429:
|
|
@@ -232,16 +224,6 @@ async def lookup_person_in_apollo_by_name(
|
|
|
232
224
|
"per_page": 10
|
|
233
225
|
}
|
|
234
226
|
|
|
235
|
-
# Build a cache key that includes full_name and company_name (if provided)
|
|
236
|
-
# so that results are correctly cached and retrieved.
|
|
237
|
-
key_item = f"lookup_person_in_apollo_by_name_{full_name}_{company_name or ''}".lower()
|
|
238
|
-
|
|
239
|
-
# Attempt to retrieve a cached response first
|
|
240
|
-
cached_response = retrieve_output("lookup_person_in_apollo_by_name", key_item)
|
|
241
|
-
if cached_response is not None:
|
|
242
|
-
logger.info(f"Cache hit for user: {full_name}, company: {company_name or ''}")
|
|
243
|
-
return cached_response
|
|
244
|
-
|
|
245
227
|
url = 'https://api.apollo.io/api/v1/mixed_people/search'
|
|
246
228
|
logger.debug(f"Making request to Apollo with payload: {data}")
|
|
247
229
|
|
|
@@ -252,7 +234,6 @@ async def lookup_person_in_apollo_by_name(
|
|
|
252
234
|
if response.status == 200:
|
|
253
235
|
result = await response.json()
|
|
254
236
|
logger.info("Successfully looked up person by name on Apollo.")
|
|
255
|
-
cache_output("lookup_person_in_apollo_by_name", key_item, result)
|
|
256
237
|
return result
|
|
257
238
|
elif response.status == 429:
|
|
258
239
|
msg = "Rate limit exceeded"
|
|
@@ -312,11 +293,6 @@ async def enrich_organization_info_from_apollo(
|
|
|
312
293
|
else:
|
|
313
294
|
headers["X-Api-Key"] = token
|
|
314
295
|
|
|
315
|
-
cached_response = retrieve_output("enrich_organization_info_from_apollo", organization_domain)
|
|
316
|
-
if cached_response is not None:
|
|
317
|
-
logger.info(f"Cache hit for organization domain: {organization_domain}")
|
|
318
|
-
return cached_response
|
|
319
|
-
|
|
320
296
|
url = f'https://api.apollo.io/api/v1/organizations/enrich?domain={organization_domain}'
|
|
321
297
|
logger.debug(f"Making GET request to Apollo for organization domain: {organization_domain}")
|
|
322
298
|
|
|
@@ -326,7 +302,6 @@ async def enrich_organization_info_from_apollo(
|
|
|
326
302
|
logger.debug(f"Received response status: {response.status}")
|
|
327
303
|
if response.status == 200:
|
|
328
304
|
result = await response.json()
|
|
329
|
-
cache_output("enrich_organization_info_from_apollo", organization_domain, result)
|
|
330
305
|
logger.info("Successfully retrieved organization info from Apollo.")
|
|
331
306
|
return result
|
|
332
307
|
elif response.status == 429:
|
|
@@ -358,22 +333,12 @@ async def enrich_organization_info_from_apollo(
|
|
|
358
333
|
)
|
|
359
334
|
async def fetch_apollo_data(session, url: str, headers: Dict[str, str], payload: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
360
335
|
logger.info("Entering fetch_apollo_data")
|
|
361
|
-
|
|
362
|
-
key_hash = hashlib.sha256(key_data.encode()).hexdigest()
|
|
363
|
-
logger.debug(f"Cache key hash: {key_hash}")
|
|
364
|
-
|
|
365
|
-
cached_response = retrieve_output("fetch_apollo_data", key_hash)
|
|
366
|
-
if cached_response is not None:
|
|
367
|
-
logger.info("Cache hit for fetch_apollo_data.")
|
|
368
|
-
return cached_response
|
|
369
|
-
|
|
370
|
-
logger.debug("No cache hit. Making POST request to Apollo.")
|
|
336
|
+
logger.debug("Making POST request to Apollo.")
|
|
371
337
|
async with session.post(url, headers=headers, json=payload) as response:
|
|
372
338
|
logger.debug(f"Received response status: {response.status}")
|
|
373
339
|
if response.status == 200:
|
|
374
340
|
result = await response.json()
|
|
375
|
-
|
|
376
|
-
logger.info("Successfully fetched data from Apollo and cached it.")
|
|
341
|
+
logger.info("Successfully fetched data from Apollo.")
|
|
377
342
|
return result
|
|
378
343
|
elif response.status == 429:
|
|
379
344
|
msg = "Rate limit exceeded"
|
|
@@ -1019,11 +984,6 @@ async def get_organization_details_from_apollo(
|
|
|
1019
984
|
else:
|
|
1020
985
|
headers["X-Api-Key"] = token
|
|
1021
986
|
|
|
1022
|
-
cached_response = retrieve_output("get_organization_details_from_apollo", organization_id)
|
|
1023
|
-
if cached_response is not None:
|
|
1024
|
-
logger.info(f"Cache hit for organization ID: {organization_id}")
|
|
1025
|
-
return cached_response
|
|
1026
|
-
|
|
1027
987
|
url = f'https://api.apollo.io/api/v1/organizations/{organization_id}'
|
|
1028
988
|
logger.debug(f"Making GET request to Apollo for organization ID: {organization_id}")
|
|
1029
989
|
|
|
@@ -1035,7 +995,6 @@ async def get_organization_details_from_apollo(
|
|
|
1035
995
|
result = await response.json()
|
|
1036
996
|
org_details = result.get('organization', {})
|
|
1037
997
|
if org_details:
|
|
1038
|
-
cache_output("get_organization_details_from_apollo", organization_id, org_details)
|
|
1039
998
|
logger.info("Successfully retrieved organization details from Apollo.")
|
|
1040
999
|
return org_details
|
|
1041
1000
|
else:
|
dhisana/utils/test_connect.py
CHANGED
|
@@ -1055,6 +1055,109 @@ async def test_jinaai(api_key: str) -> Dict[str, Any]:
|
|
|
1055
1055
|
return {"success": False, "status_code": 0, "error_message": str(exc)}
|
|
1056
1056
|
|
|
1057
1057
|
|
|
1058
|
+
async def test_firefliesai(api_key: str) -> Dict[str, Any]:
|
|
1059
|
+
"""Validate Fireflies.ai API key by querying user metadata via GraphQL."""
|
|
1060
|
+
url = "https://api.fireflies.ai/graphql"
|
|
1061
|
+
headers = {
|
|
1062
|
+
"Authorization": f"Bearer {api_key}",
|
|
1063
|
+
"Content-Type": "application/json",
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
# Try a couple of documented/observed query shapes — Fireflies occasionally
|
|
1067
|
+
# aliases the viewer field, so we fall back if the first choice is rejected.
|
|
1068
|
+
queries = [
|
|
1069
|
+
("users", {"query": "{ users { name user_id } }"}, ("data", "users")),
|
|
1070
|
+
("viewer", {"query": "query { viewer { id email } }"}, ("data", "viewer")),
|
|
1071
|
+
("me", {"query": "query { me { id email } }"}, ("data", "me")),
|
|
1072
|
+
("currentUser", {"query": "query { currentUser { id email } }"}, ("data", "currentUser")),
|
|
1073
|
+
]
|
|
1074
|
+
|
|
1075
|
+
def extract_error(payload: Optional[Dict[str, Any]]) -> Optional[str]:
|
|
1076
|
+
if not isinstance(payload, dict):
|
|
1077
|
+
return None
|
|
1078
|
+
errors = payload.get("errors")
|
|
1079
|
+
if isinstance(errors, list):
|
|
1080
|
+
messages = [
|
|
1081
|
+
err.get("message") for err in errors
|
|
1082
|
+
if isinstance(err, dict) and err.get("message")
|
|
1083
|
+
]
|
|
1084
|
+
if messages:
|
|
1085
|
+
return "; ".join(messages)
|
|
1086
|
+
elif errors:
|
|
1087
|
+
return str(errors)
|
|
1088
|
+
return (
|
|
1089
|
+
payload.get("message")
|
|
1090
|
+
or payload.get("error_description")
|
|
1091
|
+
or payload.get("error")
|
|
1092
|
+
)
|
|
1093
|
+
|
|
1094
|
+
try:
|
|
1095
|
+
timeout = aiohttp.ClientTimeout(total=10)
|
|
1096
|
+
async with aiohttp.ClientSession(timeout=timeout) as session:
|
|
1097
|
+
last_error: Optional[str] = None
|
|
1098
|
+
|
|
1099
|
+
for query_name, payload, data_path in queries:
|
|
1100
|
+
async with session.post(url, headers=headers, json=payload) as response:
|
|
1101
|
+
status = response.status
|
|
1102
|
+
data = await safe_json(response)
|
|
1103
|
+
|
|
1104
|
+
if status != 200:
|
|
1105
|
+
error_message = extract_error(data)
|
|
1106
|
+
if (
|
|
1107
|
+
error_message
|
|
1108
|
+
and "Cannot query field" in error_message
|
|
1109
|
+
and query_name != queries[-1][0]
|
|
1110
|
+
):
|
|
1111
|
+
last_error = error_message
|
|
1112
|
+
continue
|
|
1113
|
+
return {
|
|
1114
|
+
"success": False,
|
|
1115
|
+
"status_code": status,
|
|
1116
|
+
"error_message": error_message or f"Non-200 from Fireflies.ai ({query_name})",
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
if not isinstance(data, dict):
|
|
1120
|
+
return {
|
|
1121
|
+
"success": False,
|
|
1122
|
+
"status_code": status,
|
|
1123
|
+
"error_message": "Fireflies.ai returned non-JSON response.",
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
error_message = extract_error(data)
|
|
1127
|
+
if error_message:
|
|
1128
|
+
last_error = error_message
|
|
1129
|
+
# If the error indicates the field is unknown, try the next query option.
|
|
1130
|
+
if "Cannot query field" in error_message and query_name != queries[-1][0]:
|
|
1131
|
+
continue
|
|
1132
|
+
return {
|
|
1133
|
+
"success": False,
|
|
1134
|
+
"status_code": status,
|
|
1135
|
+
"error_message": error_message,
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
# Walk the data path to ensure the expected field exists.
|
|
1139
|
+
cursor: Any = data
|
|
1140
|
+
for key in data_path:
|
|
1141
|
+
if not isinstance(cursor, dict):
|
|
1142
|
+
cursor = None
|
|
1143
|
+
break
|
|
1144
|
+
cursor = cursor.get(key)
|
|
1145
|
+
|
|
1146
|
+
if cursor is not None:
|
|
1147
|
+
return {"success": True, "status_code": status, "error_message": None}
|
|
1148
|
+
|
|
1149
|
+
last_error = f"Fireflies.ai {query_name} response missing expected fields."
|
|
1150
|
+
|
|
1151
|
+
return {
|
|
1152
|
+
"success": False,
|
|
1153
|
+
"status_code": 200,
|
|
1154
|
+
"error_message": last_error or "Fireflies.ai queries did not return user data.",
|
|
1155
|
+
}
|
|
1156
|
+
except Exception as exc:
|
|
1157
|
+
logger.error(f"Fireflies.ai connectivity test failed: {exc}")
|
|
1158
|
+
return {"success": False, "status_code": 0, "error_message": str(exc)}
|
|
1159
|
+
|
|
1160
|
+
|
|
1058
1161
|
async def test_firecrawl(api_key: str) -> Dict[str, Any]:
|
|
1059
1162
|
"""Quick check for Firecrawl API key validity."""
|
|
1060
1163
|
url = "https://api.firecrawl.com/v1/scrape"
|
|
@@ -1250,6 +1353,7 @@ async def test_connectivity(tool_config: List[Dict[str, Any]]) -> Dict[str, Dict
|
|
|
1250
1353
|
"findymail": test_findyemail,
|
|
1251
1354
|
"datagma": test_datagma,
|
|
1252
1355
|
"jinaai": test_jinaai,
|
|
1356
|
+
"firefliesai": test_firefliesai,
|
|
1253
1357
|
"firecrawl": test_firecrawl,
|
|
1254
1358
|
"youtube": test_youtube,
|
|
1255
1359
|
"salesforce": test_salesforce,
|
|
@@ -12,7 +12,7 @@ dhisana/ui/components.py,sha256=4NXrAyl9tx2wWwoVYyABO-EOGnreGMvql1AkXWajIIo,1431
|
|
|
12
12
|
dhisana/utils/__init__.py,sha256=jv2YF__bseklT3OWEzlqJ5qE24c4aWd5F4r0TTjOrWQ,65
|
|
13
13
|
dhisana/utils/add_mapping.py,sha256=oq_QNqag86DhgdwINBRRXNx7SOb8Q9M-V0QLP6pTzr8,13837
|
|
14
14
|
dhisana/utils/agent_tools.py,sha256=pzBFvfhU4wfSB4zv1eiRzjmnteJnfhC5V32r_v1m38Y,2321
|
|
15
|
-
dhisana/utils/apollo_tools.py,sha256=
|
|
15
|
+
dhisana/utils/apollo_tools.py,sha256=DUg9HQBHwD9ZjqbdLP2QPWxbPSAN7aN2d_Brq4svSg0,64297
|
|
16
16
|
dhisana/utils/assistant_tool_tag.py,sha256=rYRl8ubLI7fUUIjg30XTefHBkFgRqNEVC12lF6U6Z-8,119
|
|
17
17
|
dhisana/utils/built_with_api_tools.py,sha256=TFNGhnPb2vFdveVCpjiCvE1WKe_eK95UPpR0Ha5NgMQ,10260
|
|
18
18
|
dhisana/utils/cache_output_tools.py,sha256=sSAruvUZn-WAJQ0lB9T1QjSmkm-_14AuxC9xKmcCQ0k,3428
|
|
@@ -78,7 +78,7 @@ dhisana/utils/serperdev_google_jobs.py,sha256=m5_2f_5y79FOFZz1A_go6m0hIUfbbAoZ0Y
|
|
|
78
78
|
dhisana/utils/serperdev_local_business.py,sha256=JoZfTg58Hojv61cyuwA2lcnPdLT1lawnWaBNrUYWnuQ,6447
|
|
79
79
|
dhisana/utils/serperdev_search.py,sha256=_iBKIfHMq4gFv5StYz58eArriygoi1zW6VnLlux8vto,9363
|
|
80
80
|
dhisana/utils/smtp_email_tools.py,sha256=kx0VSa0JQyVLD020oARCHhOBZJxDwMIri1Z7jPN0nP4,15843
|
|
81
|
-
dhisana/utils/test_connect.py,sha256
|
|
81
|
+
dhisana/utils/test_connect.py,sha256=AhaGFV1-E5YhzkWyDtXypuAeFxgB6tIBHAs5eSSL-6k,65210
|
|
82
82
|
dhisana/utils/trasform_json.py,sha256=s48DoyzVVCI4dTvSUwF5X-exX6VH6nPWrjbUENkYuNE,6979
|
|
83
83
|
dhisana/utils/web_download_parse_tools.py,sha256=ouXwH7CmjcRjoBfP5BWat86MvcGO-8rLCmWQe_eZKjc,7810
|
|
84
84
|
dhisana/utils/workflow_code_model.py,sha256=YPWse5vBb3O6Km2PvKh1Q3AB8qBkzLt1CrR5xOL9Mro,99
|
|
@@ -92,8 +92,8 @@ dhisana/workflow/agent.py,sha256=esv7_i_XuMkV2j1nz_UlsHov_m6X5WZZiZm_tG4OBHU,565
|
|
|
92
92
|
dhisana/workflow/flow.py,sha256=xWE3qQbM7j2B3FH8XnY3zOL_QXX4LbTW4ArndnEYJE0,1638
|
|
93
93
|
dhisana/workflow/task.py,sha256=HlWz9mtrwLYByoSnePOemBUBrMEcj7KbgNjEE1oF5wo,1830
|
|
94
94
|
dhisana/workflow/test.py,sha256=kwW8jWqSBNcRmoyaxlTuZCMOpGJpTbJQgHI7gSjwdzM,3399
|
|
95
|
-
dhisana-0.0.1.
|
|
96
|
-
dhisana-0.0.1.
|
|
97
|
-
dhisana-0.0.1.
|
|
98
|
-
dhisana-0.0.1.
|
|
99
|
-
dhisana-0.0.1.
|
|
95
|
+
dhisana-0.0.1.dev224.dist-info/METADATA,sha256=KjVLbvbvTN_2Gsf_Q3mP3vINRSR2Nvkhi78oN5uk6R8,1190
|
|
96
|
+
dhisana-0.0.1.dev224.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
97
|
+
dhisana-0.0.1.dev224.dist-info/entry_points.txt,sha256=jujxteZmNI9EkEaK-pOCoWuBujU8TCevdkfl9ZcKHek,49
|
|
98
|
+
dhisana-0.0.1.dev224.dist-info/top_level.txt,sha256=NETTHt6YifG_P7XtRHbQiXZlgSFk9Qh9aR-ng1XTf4s,8
|
|
99
|
+
dhisana-0.0.1.dev224.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|