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.
Files changed (115) hide show
  1. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/PKG-INFO +1 -1
  2. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/setup.py +1 -1
  3. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/google_oauth_tools.py +200 -0
  4. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana.egg-info/PKG-INFO +1 -1
  5. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/README.md +0 -0
  6. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/pyproject.toml +0 -0
  7. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/setup.cfg +0 -0
  8. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/__init__.py +0 -0
  9. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/cli/__init__.py +0 -0
  10. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/cli/cli.py +0 -0
  11. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/cli/datasets.py +0 -0
  12. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/cli/models.py +0 -0
  13. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/cli/predictions.py +0 -0
  14. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/schemas/__init__.py +0 -0
  15. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/schemas/common.py +0 -0
  16. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/schemas/sales.py +0 -0
  17. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/ui/__init__.py +0 -0
  18. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/ui/components.py +0 -0
  19. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/__init__.py +0 -0
  20. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/add_mapping.py +0 -0
  21. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/agent_tools.py +0 -0
  22. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/apollo_tools.py +0 -0
  23. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/assistant_tool_tag.py +0 -0
  24. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/built_with_api_tools.py +0 -0
  25. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/cache_output_tools.py +0 -0
  26. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/cache_output_tools_local.py +0 -0
  27. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/check_email_validity_tools.py +0 -0
  28. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/check_for_intent_signal.py +0 -0
  29. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/check_linkedin_url_validity.py +0 -0
  30. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/clay_tools.py +0 -0
  31. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/clean_properties.py +0 -0
  32. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/company_utils.py +0 -0
  33. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/compose_salesnav_query.py +0 -0
  34. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/compose_search_query.py +0 -0
  35. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/compose_three_step_workflow.py +0 -0
  36. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/composite_tools.py +0 -0
  37. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/dataframe_tools.py +0 -0
  38. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/domain_parser.py +0 -0
  39. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/email_parse_helpers.py +0 -0
  40. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/email_provider.py +0 -0
  41. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/enrich_lead_information.py +0 -0
  42. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/extract_email_content_for_llm.py +0 -0
  43. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/fetch_openai_config.py +0 -0
  44. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/field_validators.py +0 -0
  45. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/g2_tools.py +0 -0
  46. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/generate_content.py +0 -0
  47. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/generate_email.py +0 -0
  48. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/generate_email_response.py +0 -0
  49. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/generate_flow.py +0 -0
  50. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/generate_leads_salesnav.py +0 -0
  51. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/generate_linkedin_connect_message.py +0 -0
  52. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/generate_linkedin_response_message.py +0 -0
  53. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/generate_structured_output_internal.py +0 -0
  54. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/google_custom_search.py +0 -0
  55. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/google_workspace_tools.py +0 -0
  56. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/hubspot_clearbit.py +0 -0
  57. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/hubspot_crm_tools.py +0 -0
  58. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/instantly_tools.py +0 -0
  59. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/linkedin_crawler.py +0 -0
  60. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/lusha_tools.py +0 -0
  61. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/mailgun_tools.py +0 -0
  62. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/microsoft365_tools.py +0 -0
  63. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/openai_assistant_and_file_utils.py +0 -0
  64. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/openai_helpers.py +0 -0
  65. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/openapi_spec_to_tools.py +0 -0
  66. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/openapi_tool/__init__.py +0 -0
  67. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/openapi_tool/api_models.py +0 -0
  68. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/openapi_tool/convert_openai_spec_to_tool.py +0 -0
  69. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/openapi_tool/openapi_tool.py +0 -0
  70. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/parse_linkedin_messages_txt.py +0 -0
  71. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/profile.py +0 -0
  72. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/proxy_curl_tools.py +0 -0
  73. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/proxycurl_search_leads.py +0 -0
  74. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/python_function_to_tools.py +0 -0
  75. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/research_lead.py +0 -0
  76. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/sales_navigator_crawler.py +0 -0
  77. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/salesforce_crm_tools.py +0 -0
  78. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/search_router.py +0 -0
  79. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/search_router_jobs.py +0 -0
  80. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/sendgrid_tools.py +0 -0
  81. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/serarch_router_local_business.py +0 -0
  82. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/serpapi_additional_tools.py +0 -0
  83. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/serpapi_google_jobs.py +0 -0
  84. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/serpapi_google_search.py +0 -0
  85. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/serpapi_local_business_search.py +0 -0
  86. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/serpapi_search_tools.py +0 -0
  87. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/serperdev_google_jobs.py +0 -0
  88. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/serperdev_local_business.py +0 -0
  89. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/serperdev_search.py +0 -0
  90. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/smtp_email_tools.py +0 -0
  91. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/test_connect.py +0 -0
  92. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/trasform_json.py +0 -0
  93. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/web_download_parse_tools.py +0 -0
  94. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/workflow_code_model.py +0 -0
  95. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/utils/zoominfo_tools.py +0 -0
  96. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/workflow/__init__.py +0 -0
  97. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/workflow/agent.py +0 -0
  98. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/workflow/flow.py +0 -0
  99. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/workflow/task.py +0 -0
  100. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana/workflow/test.py +0 -0
  101. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana.egg-info/SOURCES.txt +0 -0
  102. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana.egg-info/dependency_links.txt +0 -0
  103. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana.egg-info/entry_points.txt +0 -0
  104. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana.egg-info/requires.txt +0 -0
  105. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/src/dhisana.egg-info/top_level.txt +0 -0
  106. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/tests/test_agent_tools.py +0 -0
  107. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/tests/test_apollo_company_search.py +0 -0
  108. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/tests/test_connectivity.py +0 -0
  109. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/tests/test_google_document.py +0 -0
  110. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/tests/test_hubspot_call_logs.py +0 -0
  111. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/tests/test_linkedin_serper.py +0 -0
  112. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/tests/test_mcp_connectivity.py +0 -0
  113. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/tests/test_proxycurl_get_company_search_id.py +0 -0
  114. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/tests/test_proxycurl_job_count.py +0 -0
  115. {dhisana-0.0.1.dev208 → dhisana-0.0.1.dev209}/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.dev208
3
+ Version: 0.0.1.dev209
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-dev208',
5
+ version='0.0.1-dev209',
6
6
  description='A Python SDK for Dhisana AI Platform',
7
7
  author='Admin',
8
8
  author_email='contact@dhisana.ai',
@@ -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)})]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dhisana
3
- Version: 0.0.1.dev208
3
+ Version: 0.0.1.dev209
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