dhisana 0.0.1.dev208__tar.gz → 0.0.1.dev209__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.dev208 → dhisana-0.0.1.dev209}/PKG-INFO +1 -1
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/setup.py +1 -1
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/google_oauth_tools.py +200 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana.egg-info/PKG-INFO +1 -1
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/README.md +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/pyproject.toml +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/setup.cfg +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/__init__.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/cli/__init__.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/cli/cli.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/cli/datasets.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/cli/models.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/cli/predictions.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/schemas/__init__.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/schemas/common.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/schemas/sales.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/ui/__init__.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/ui/components.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/__init__.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/add_mapping.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/agent_tools.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/apollo_tools.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/assistant_tool_tag.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/built_with_api_tools.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/cache_output_tools.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/cache_output_tools_local.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/check_email_validity_tools.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/check_for_intent_signal.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/check_linkedin_url_validity.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/clay_tools.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/clean_properties.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/company_utils.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/compose_salesnav_query.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/compose_search_query.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/compose_three_step_workflow.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/composite_tools.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/dataframe_tools.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/domain_parser.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/email_parse_helpers.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/email_provider.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/enrich_lead_information.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/extract_email_content_for_llm.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/fetch_openai_config.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/field_validators.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/g2_tools.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/generate_content.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/generate_email.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/generate_email_response.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/generate_flow.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/generate_leads_salesnav.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/generate_linkedin_connect_message.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/generate_linkedin_response_message.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/generate_structured_output_internal.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/google_custom_search.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/google_workspace_tools.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/hubspot_clearbit.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/hubspot_crm_tools.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/instantly_tools.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/linkedin_crawler.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/lusha_tools.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/mailgun_tools.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/microsoft365_tools.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/openai_assistant_and_file_utils.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/openai_helpers.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/openapi_spec_to_tools.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/openapi_tool/__init__.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/openapi_tool/api_models.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/openapi_tool/convert_openai_spec_to_tool.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/openapi_tool/openapi_tool.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/parse_linkedin_messages_txt.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/profile.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/proxy_curl_tools.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/proxycurl_search_leads.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/python_function_to_tools.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/research_lead.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/sales_navigator_crawler.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/salesforce_crm_tools.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/search_router.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/search_router_jobs.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/sendgrid_tools.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/serarch_router_local_business.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/serpapi_additional_tools.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/serpapi_google_jobs.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/serpapi_google_search.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/serpapi_local_business_search.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/serpapi_search_tools.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/serperdev_google_jobs.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/serperdev_local_business.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/serperdev_search.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/smtp_email_tools.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/test_connect.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/trasform_json.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/web_download_parse_tools.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/workflow_code_model.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/zoominfo_tools.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/workflow/__init__.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/workflow/agent.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/workflow/flow.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/workflow/task.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/workflow/test.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana.egg-info/SOURCES.txt +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana.egg-info/dependency_links.txt +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana.egg-info/entry_points.txt +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana.egg-info/requires.txt +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana.egg-info/top_level.txt +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/tests/test_agent_tools.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/tests/test_apollo_company_search.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/tests/test_connectivity.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/tests/test_google_document.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/tests/test_hubspot_call_logs.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/tests/test_linkedin_serper.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/tests/test_mcp_connectivity.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/tests/test_proxycurl_get_company_search_id.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/tests/test_proxycurl_job_count.py +0 -0
- {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/tests/test_structured_output_with_mcp.py +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import base64
|
|
2
2
|
import json
|
|
3
3
|
import logging
|
|
4
|
+
import re
|
|
4
5
|
from email.mime.text import MIMEText
|
|
5
6
|
from typing import Any, Dict, List, Optional
|
|
6
7
|
|
|
@@ -19,6 +20,9 @@ from dhisana.utils.email_parse_helpers import (
|
|
|
19
20
|
convert_date_to_iso,
|
|
20
21
|
extract_email_body_in_plain_text,
|
|
21
22
|
)
|
|
23
|
+
from dhisana.utils.assistant_tool_tag import assistant_tool
|
|
24
|
+
from dhisana.utils.cache_output_tools import retrieve_output, cache_output
|
|
25
|
+
from typing import Optional as _Optional # avoid name clash in wrappers
|
|
22
26
|
|
|
23
27
|
|
|
24
28
|
def get_google_access_token(tool_config: Optional[List[Dict]] = None) -> str:
|
|
@@ -253,3 +257,199 @@ async def reply_to_email_google_oauth_async(
|
|
|
253
257
|
"email_labels": sent.get("labelIds", []),
|
|
254
258
|
}
|
|
255
259
|
|
|
260
|
+
|
|
261
|
+
# ---------------------------------------------------------------------------
|
|
262
|
+
# Google Calendar (OAuth per-user)
|
|
263
|
+
# ---------------------------------------------------------------------------
|
|
264
|
+
|
|
265
|
+
@assistant_tool
|
|
266
|
+
async def get_calendar_events_using_google_oauth_async(
|
|
267
|
+
start_date: str,
|
|
268
|
+
end_date: str,
|
|
269
|
+
tool_config: Optional[List[Dict]] = None,
|
|
270
|
+
) -> List[Dict[str, Any]]:
|
|
271
|
+
"""
|
|
272
|
+
Retrieve events from the user's primary Google Calendar using a per-user OAuth token.
|
|
273
|
+
|
|
274
|
+
start_date, end_date: 'YYYY-MM-DD' strings (inclusive start, inclusive end day as 23:59:59Z).
|
|
275
|
+
Returns a list of event dicts from the Calendar API.
|
|
276
|
+
"""
|
|
277
|
+
token = get_google_access_token(tool_config)
|
|
278
|
+
headers = {"Authorization": f"Bearer {token}"}
|
|
279
|
+
url = "https://www.googleapis.com/calendar/v3/calendars/primary/events"
|
|
280
|
+
|
|
281
|
+
time_min = f"{start_date}T00:00:00Z"
|
|
282
|
+
time_max = f"{end_date}T23:59:59Z"
|
|
283
|
+
params = {
|
|
284
|
+
"timeMin": time_min,
|
|
285
|
+
"timeMax": time_max,
|
|
286
|
+
"maxResults": 10,
|
|
287
|
+
"singleEvents": True,
|
|
288
|
+
"orderBy": "startTime",
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async with httpx.AsyncClient(timeout=30) as client:
|
|
292
|
+
resp = await client.get(url, headers=headers, params=params)
|
|
293
|
+
resp.raise_for_status()
|
|
294
|
+
data = resp.json() or {}
|
|
295
|
+
events = data.get("items", [])
|
|
296
|
+
if not events:
|
|
297
|
+
logging.info("No upcoming events found within the specified range (OAuth).")
|
|
298
|
+
return events
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
# ---------------------------------------------------------------------------
|
|
302
|
+
# Google Sheets and Docs (OAuth per-user)
|
|
303
|
+
# ---------------------------------------------------------------------------
|
|
304
|
+
|
|
305
|
+
def _get_sheet_id_from_url(sheet_url: str) -> str:
|
|
306
|
+
match = re.search(r"/d/([a-zA-Z0-9-_]+)/", sheet_url)
|
|
307
|
+
if not match:
|
|
308
|
+
raise ValueError("Could not extract spreadsheet ID from the provided URL.")
|
|
309
|
+
return match.group(1)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def _get_document_id_from_url(doc_url: str) -> str:
|
|
313
|
+
match = re.search(r"/d/([a-zA-Z0-9-_]+)/", doc_url)
|
|
314
|
+
if not match:
|
|
315
|
+
raise ValueError("Could not extract document ID from the provided URL.")
|
|
316
|
+
return match.group(1)
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
@assistant_tool
|
|
320
|
+
async def read_google_sheet_using_google_oauth(
|
|
321
|
+
sheet_url: str,
|
|
322
|
+
range_name: str,
|
|
323
|
+
tool_config: Optional[List[Dict]] = None,
|
|
324
|
+
) -> List[List[str]]:
|
|
325
|
+
"""
|
|
326
|
+
Read data from a Google Sheet using the connected user's OAuth token.
|
|
327
|
+
|
|
328
|
+
If range_name is empty, reads the first sheet tab by fetching spreadsheet metadata.
|
|
329
|
+
"""
|
|
330
|
+
token = get_google_access_token(tool_config)
|
|
331
|
+
headers = {"Authorization": f"Bearer {token}"}
|
|
332
|
+
|
|
333
|
+
spreadsheet_id = _get_sheet_id_from_url(sheet_url)
|
|
334
|
+
|
|
335
|
+
# Default range to first sheet title if not supplied
|
|
336
|
+
if not range_name:
|
|
337
|
+
meta_url = f"https://sheets.googleapis.com/v4/spreadsheets/{spreadsheet_id}"
|
|
338
|
+
async with httpx.AsyncClient(timeout=30) as client:
|
|
339
|
+
meta_resp = await client.get(meta_url, headers=headers)
|
|
340
|
+
meta_resp.raise_for_status()
|
|
341
|
+
meta = meta_resp.json() or {}
|
|
342
|
+
sheets = meta.get("sheets", [])
|
|
343
|
+
if not sheets:
|
|
344
|
+
return []
|
|
345
|
+
range_name = (sheets[0].get("properties") or {}).get("title") or "Sheet1"
|
|
346
|
+
|
|
347
|
+
values_url = f"https://sheets.googleapis.com/v4/spreadsheets/{spreadsheet_id}/values/{range_name}"
|
|
348
|
+
async with httpx.AsyncClient(timeout=30) as client:
|
|
349
|
+
val_resp = await client.get(values_url, headers=headers)
|
|
350
|
+
val_resp.raise_for_status()
|
|
351
|
+
data = val_resp.json() or {}
|
|
352
|
+
return data.get("values", [])
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
@assistant_tool
|
|
356
|
+
async def read_google_document_using_google_oauth(
|
|
357
|
+
doc_url: str,
|
|
358
|
+
tool_config: Optional[List[Dict]] = None,
|
|
359
|
+
) -> str:
|
|
360
|
+
"""
|
|
361
|
+
Read text content from a Google Doc using the connected user's OAuth token.
|
|
362
|
+
Concatenates all text runs in the document body.
|
|
363
|
+
"""
|
|
364
|
+
token = get_google_access_token(tool_config)
|
|
365
|
+
headers = {"Authorization": f"Bearer {token}"}
|
|
366
|
+
|
|
367
|
+
document_id = _get_document_id_from_url(doc_url)
|
|
368
|
+
url = f"https://docs.googleapis.com/v1/documents/{document_id}"
|
|
369
|
+
|
|
370
|
+
async with httpx.AsyncClient(timeout=30) as client:
|
|
371
|
+
resp = await client.get(url, headers=headers)
|
|
372
|
+
resp.raise_for_status()
|
|
373
|
+
doc = resp.json() or {}
|
|
374
|
+
|
|
375
|
+
content = (doc.get("body") or {}).get("content", [])
|
|
376
|
+
parts: List[str] = []
|
|
377
|
+
for element in content:
|
|
378
|
+
paragraph = element.get("paragraph")
|
|
379
|
+
if not paragraph:
|
|
380
|
+
continue
|
|
381
|
+
for elem in paragraph.get("elements", []) or []:
|
|
382
|
+
text_run = elem.get("textRun")
|
|
383
|
+
if text_run:
|
|
384
|
+
parts.append(text_run.get("content", ""))
|
|
385
|
+
|
|
386
|
+
return "".join(parts)
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
@assistant_tool
|
|
390
|
+
async def search_google_places_using_google_oauth(
|
|
391
|
+
query: str,
|
|
392
|
+
location_bias: dict = None,
|
|
393
|
+
number_of_results: int = 3,
|
|
394
|
+
tool_config: _Optional[List[Dict]] = None,
|
|
395
|
+
) -> List[str]:
|
|
396
|
+
"""
|
|
397
|
+
Search Google Places (New) with a per-user OAuth token.
|
|
398
|
+
|
|
399
|
+
- Requires that the OAuth token has Maps/Places access enabled for the project.
|
|
400
|
+
- Returns a list of JSON strings, each being a place object.
|
|
401
|
+
"""
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
token = get_google_access_token(tool_config)
|
|
405
|
+
url = "https://places.googleapis.com/v1/places:searchText"
|
|
406
|
+
headers = {
|
|
407
|
+
"Content-Type": "application/json",
|
|
408
|
+
"Authorization": f"Bearer {token}",
|
|
409
|
+
# Field mask is required to limit returned fields
|
|
410
|
+
"X-Goog-FieldMask": (
|
|
411
|
+
"places.displayName,places.formattedAddress,places.location,"
|
|
412
|
+
"places.websiteUri,places.rating,places.reviews"
|
|
413
|
+
),
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
body: Dict[str, Any] = {"textQuery": query}
|
|
417
|
+
if location_bias:
|
|
418
|
+
body["locationBias"] = {
|
|
419
|
+
"circle": {
|
|
420
|
+
"center": {
|
|
421
|
+
"latitude": location_bias.get("latitude"),
|
|
422
|
+
"longitude": location_bias.get("longitude"),
|
|
423
|
+
},
|
|
424
|
+
"radius": location_bias.get("radius", 5000),
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
# Cache key based on query, count and bias
|
|
429
|
+
bias_str = json.dumps(location_bias, sort_keys=True) if location_bias else "None"
|
|
430
|
+
cache_key = f"oauth_places:{query}:{number_of_results}:{bias_str}"
|
|
431
|
+
cached = retrieve_output("search_google_places_oauth", cache_key)
|
|
432
|
+
if cached is not None:
|
|
433
|
+
return cached
|
|
434
|
+
|
|
435
|
+
async with httpx.AsyncClient(timeout=30) as client:
|
|
436
|
+
try:
|
|
437
|
+
resp = await client.post(url, headers=headers, json=body)
|
|
438
|
+
if resp.status_code == 429:
|
|
439
|
+
return [json.dumps({"error": "Rate limit exceeded (429)"})]
|
|
440
|
+
resp.raise_for_status()
|
|
441
|
+
data = resp.json() or {}
|
|
442
|
+
places = (data.get("places") or [])[: max(0, int(number_of_results))]
|
|
443
|
+
out = [json.dumps(p) for p in places]
|
|
444
|
+
cache_output("search_google_places_oauth", cache_key, out)
|
|
445
|
+
return out
|
|
446
|
+
except httpx.HTTPStatusError as exc:
|
|
447
|
+
try:
|
|
448
|
+
err_json = exc.response.json()
|
|
449
|
+
except Exception:
|
|
450
|
+
err_json = {"status": exc.response.status_code, "text": exc.response.text}
|
|
451
|
+
logging.warning(f"Places OAuth request failed: {err_json}")
|
|
452
|
+
return [json.dumps({"error": err_json})]
|
|
453
|
+
except Exception as e:
|
|
454
|
+
logging.exception("Places OAuth request failed")
|
|
455
|
+
return [json.dumps({"error": str(e)})]
|
|
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
|
{dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/check_email_validity_tools.py
RENAMED
|
File without changes
|
|
File without changes
|
{dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/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.dev208 → dhisana-0.0.1.dev209}/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.dev208 → dhisana-0.0.1.dev209}/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
|
{dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/generate_linkedin_connect_message.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
|
{dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/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.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/openapi_tool/openapi_tool.py
RENAMED
|
File without changes
|
{dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/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.dev208 → dhisana-0.0.1.dev209}/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.dev208 → dhisana-0.0.1.dev209}/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
|