dhisana 0.0.1.dev7__tar.gz → 0.0.1.dev9__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.dev7 → dhisana-0.0.1.dev9}/PKG-INFO +3 -1
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/setup.py +4 -2
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/src/dhisana/ui/components.py +129 -45
- dhisana-0.0.1.dev9/src/dhisana/utils/agent_tools.py +48 -0
- dhisana-0.0.1.dev9/src/dhisana/utils/apollo_tools.py +121 -0
- dhisana-0.0.1.dev9/src/dhisana/utils/check_email_validity_tools.py +36 -0
- dhisana-0.0.1.dev9/src/dhisana/utils/dataframe_tools.py +202 -0
- dhisana-0.0.1.dev7/src/dhisana/utils/agent_tools.py → dhisana-0.0.1.dev9/src/dhisana/utils/google_workspace_tools.py +337 -271
- dhisana-0.0.1.dev9/src/dhisana/utils/hubspot_crm_tools.py +598 -0
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/src/dhisana/utils/linkedin_crawler.py +8 -9
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/src/dhisana/utils/openai_helpers.py +16 -8
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/src/dhisana/utils/openapi_spec_to_tools.py +16 -6
- dhisana-0.0.1.dev9/src/dhisana/utils/python_function_to_tools.py +82 -0
- dhisana-0.0.1.dev9/src/dhisana/utils/salesforce_crm_tools.py +202 -0
- dhisana-0.0.1.dev9/src/dhisana/utils/tools_json.py +2 -0
- dhisana-0.0.1.dev9/src/dhisana/utils/web_download_parse_tools.py +35 -0
- dhisana-0.0.1.dev9/src/dhisana/workflow/__init__.py +1 -0
- dhisana-0.0.1.dev9/src/dhisana/workflow/task.py +60 -0
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/src/dhisana.egg-info/PKG-INFO +3 -1
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/src/dhisana.egg-info/SOURCES.txt +10 -0
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/src/dhisana.egg-info/requires.txt +2 -0
- dhisana-0.0.1.dev7/src/dhisana/utils/tools_json.py +0 -123
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/README.md +0 -0
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/pyproject.toml +0 -0
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/setup.cfg +0 -0
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/src/dhisana/__init__.py +0 -0
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/src/dhisana/cli/__init__.py +0 -0
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/src/dhisana/cli/cli.py +0 -0
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/src/dhisana/cli/datasets.py +0 -0
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/src/dhisana/cli/models.py +0 -0
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/src/dhisana/cli/predictions.py +0 -0
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/src/dhisana/ui/__init__.py +0 -0
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/src/dhisana/utils/__init__.py +0 -0
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/src/dhisana/utils/assistant_tool_tag.py +0 -0
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/src/dhisana/utils/openapi_tool/__init__.py +0 -0
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/src/dhisana/utils/openapi_tool/api_models.py +0 -0
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/src/dhisana/utils/openapi_tool/convert_openai_spec_to_tool.py +0 -0
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/src/dhisana/utils/openapi_tool/openapi_tool.py +0 -0
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/src/dhisana.egg-info/dependency_links.txt +0 -0
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/src/dhisana.egg-info/entry_points.txt +0 -0
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/src/dhisana.egg-info/top_level.txt +0 -0
- {dhisana-0.0.1.dev7 → dhisana-0.0.1.dev9}/tests/test_agent_tools.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dhisana
|
|
3
|
-
Version: 0.0.1.
|
|
3
|
+
Version: 0.0.1.dev9
|
|
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
|
|
@@ -23,3 +23,5 @@ Requires-Dist: uvicorn[standard]
|
|
|
23
23
|
Requires-Dist: aiohttp
|
|
24
24
|
Requires-Dist: openapi_pydantic
|
|
25
25
|
Requires-Dist: pandas
|
|
26
|
+
Requires-Dist: simple_salesforce
|
|
27
|
+
Requires-Dist: backoff
|
|
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|
|
2
2
|
|
|
3
3
|
setup(
|
|
4
4
|
name='dhisana',
|
|
5
|
-
version='0.0.1-
|
|
5
|
+
version='0.0.1-dev9',
|
|
6
6
|
description='A Python SDK for Dhisana AI Platform',
|
|
7
7
|
author='Admin',
|
|
8
8
|
author_email='contact@dhisana.ai',
|
|
@@ -22,7 +22,9 @@ setup(
|
|
|
22
22
|
'uvicorn[standard]',
|
|
23
23
|
'aiohttp',
|
|
24
24
|
'openapi_pydantic',
|
|
25
|
-
'pandas'
|
|
25
|
+
'pandas',
|
|
26
|
+
'simple_salesforce',
|
|
27
|
+
'backoff'
|
|
26
28
|
],
|
|
27
29
|
entry_points={
|
|
28
30
|
'console_scripts': [
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
from typing import List, Dict, Any, Optional
|
|
2
|
-
|
|
1
|
+
from typing import List, Dict, Any, Optional, Union
|
|
3
2
|
|
|
4
3
|
class Component:
|
|
5
4
|
def to_dict(self) -> Dict[str, Any]:
|
|
6
5
|
raise NotImplementedError("Must implement to_dict method.")
|
|
7
6
|
|
|
8
|
-
|
|
9
7
|
class Header(Component):
|
|
10
8
|
def __init__(self, title: str, subtitle: Optional[str] = None, logo: Optional[str] = None):
|
|
11
9
|
self.title = title
|
|
@@ -22,7 +20,6 @@ class Header(Component):
|
|
|
22
20
|
},
|
|
23
21
|
}
|
|
24
22
|
|
|
25
|
-
|
|
26
23
|
class Footer(Component):
|
|
27
24
|
def __init__(self, content: str):
|
|
28
25
|
self.content = content
|
|
@@ -35,7 +32,6 @@ class Footer(Component):
|
|
|
35
32
|
},
|
|
36
33
|
}
|
|
37
34
|
|
|
38
|
-
|
|
39
35
|
class Sidebar(Component):
|
|
40
36
|
def __init__(self, items: List[str]):
|
|
41
37
|
self.items = items
|
|
@@ -48,19 +44,6 @@ class Sidebar(Component):
|
|
|
48
44
|
},
|
|
49
45
|
}
|
|
50
46
|
|
|
51
|
-
class Text(Component):
|
|
52
|
-
def __init__(self, content: str):
|
|
53
|
-
self.content = content
|
|
54
|
-
|
|
55
|
-
def to_dict(self):
|
|
56
|
-
return {
|
|
57
|
-
'type': 'text',
|
|
58
|
-
'properties': {
|
|
59
|
-
'content': self.content,
|
|
60
|
-
},
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
64
47
|
class MainContent(Component):
|
|
65
48
|
def __init__(self, children: List[Component]):
|
|
66
49
|
self.children = children
|
|
@@ -88,29 +71,39 @@ class ChatWindow(Component):
|
|
|
88
71
|
},
|
|
89
72
|
}
|
|
90
73
|
|
|
91
|
-
|
|
92
74
|
class DataTable(Component):
|
|
93
75
|
def __init__(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
76
|
+
self,
|
|
77
|
+
columns: List[Dict[str, Any]],
|
|
78
|
+
data_source: str,
|
|
79
|
+
actions: Optional[List[Dict[str, Any]]] = None,
|
|
80
|
+
row_selection: Optional[bool] = False,
|
|
81
|
+
selected_row_keys: Optional[List[str]] = None,
|
|
82
|
+
on_selection_change: Optional[str] = None,
|
|
83
|
+
title: Optional[str] = None,
|
|
84
|
+
):
|
|
85
|
+
self.title = title
|
|
99
86
|
self.columns = columns
|
|
100
|
-
self.data_source = data_source
|
|
87
|
+
self.data_source = data_source # Should be a reference to data in dataContext
|
|
101
88
|
self.actions = actions or []
|
|
89
|
+
self.row_selection = row_selection
|
|
90
|
+
self.selected_row_keys = selected_row_keys
|
|
91
|
+
self.on_selection_change = on_selection_change
|
|
102
92
|
|
|
103
93
|
def to_dict(self):
|
|
104
94
|
return {
|
|
105
95
|
'type': 'data-table',
|
|
106
96
|
'properties': {
|
|
97
|
+
'title': self.title,
|
|
107
98
|
'columns': self.columns,
|
|
108
|
-
'dataSource': self.data_source,
|
|
99
|
+
'dataSource': self.data_source, # Should be in the form '{{dataKey}}'
|
|
109
100
|
'actions': self.actions,
|
|
101
|
+
'rowSelection': self.row_selection,
|
|
102
|
+
'selectedRowKeys': self.selected_row_keys,
|
|
103
|
+
'onSelectionChange': self.on_selection_change,
|
|
110
104
|
},
|
|
111
105
|
}
|
|
112
106
|
|
|
113
|
-
|
|
114
107
|
class Chart(Component):
|
|
115
108
|
def __init__(self, chart_type: str, data_source: str, options: Optional[Dict[str, Any]] = None):
|
|
116
109
|
self.chart_type = chart_type
|
|
@@ -127,7 +120,6 @@ class Chart(Component):
|
|
|
127
120
|
},
|
|
128
121
|
}
|
|
129
122
|
|
|
130
|
-
|
|
131
123
|
class Form(Component):
|
|
132
124
|
def __init__(self, children: List[Component], on_submit: List[str]):
|
|
133
125
|
self.children = children
|
|
@@ -142,43 +134,51 @@ class Form(Component):
|
|
|
142
134
|
'children': [child.to_dict() for child in self.children],
|
|
143
135
|
}
|
|
144
136
|
|
|
145
|
-
|
|
146
137
|
class FormItem(Component):
|
|
147
|
-
def __init__(self, label: str, children: List[Component]):
|
|
138
|
+
def __init__(self, label: str, children: List[Component], visible: bool = True):
|
|
148
139
|
self.label = label
|
|
149
140
|
self.children = children
|
|
141
|
+
self.visible = visible
|
|
150
142
|
|
|
151
143
|
def to_dict(self):
|
|
152
144
|
return {
|
|
153
145
|
'type': 'form-item',
|
|
154
146
|
'properties': {
|
|
155
147
|
'label': self.label,
|
|
148
|
+
'visible': self.visible,
|
|
156
149
|
},
|
|
157
150
|
'children': [child.to_dict() for child in self.children],
|
|
158
151
|
}
|
|
159
152
|
|
|
160
|
-
|
|
161
153
|
class Input(Component):
|
|
162
|
-
def __init__(self, name: str, placeholder: str = '', required: bool = False):
|
|
154
|
+
def __init__(self, name: str, type: str = 'text', placeholder: str = '', required: bool = False, value: str = None, checked: bool = False):
|
|
163
155
|
self.name = name
|
|
156
|
+
self.type = type
|
|
164
157
|
self.placeholder = placeholder
|
|
165
158
|
self.required = required
|
|
159
|
+
self.value = value
|
|
160
|
+
self.checked = checked
|
|
166
161
|
|
|
167
162
|
def to_dict(self):
|
|
168
163
|
return {
|
|
169
164
|
'type': 'input',
|
|
170
165
|
'properties': {
|
|
171
166
|
'name': self.name,
|
|
167
|
+
'type': self.type,
|
|
172
168
|
'placeholder': self.placeholder,
|
|
173
169
|
'required': self.required,
|
|
170
|
+
'value': self.value,
|
|
171
|
+
'checked': self.checked,
|
|
174
172
|
},
|
|
175
173
|
}
|
|
176
174
|
|
|
177
|
-
|
|
178
175
|
class TextArea(Component):
|
|
179
|
-
def __init__(self, name: str, placeholder: str = ''):
|
|
176
|
+
def __init__(self, name: str, placeholder: str = '', value: str = None, required: bool = False, rows: int = 3):
|
|
180
177
|
self.name = name
|
|
181
178
|
self.placeholder = placeholder
|
|
179
|
+
self.value = value
|
|
180
|
+
self.required = required
|
|
181
|
+
self.rows = rows
|
|
182
182
|
|
|
183
183
|
def to_dict(self):
|
|
184
184
|
return {
|
|
@@ -186,14 +186,17 @@ class TextArea(Component):
|
|
|
186
186
|
'properties': {
|
|
187
187
|
'name': self.name,
|
|
188
188
|
'placeholder': self.placeholder,
|
|
189
|
+
'value': self.value,
|
|
190
|
+
'required': self.required,
|
|
191
|
+
'rows': self.rows,
|
|
189
192
|
},
|
|
190
193
|
}
|
|
191
194
|
|
|
192
|
-
|
|
193
195
|
class Upload(Component):
|
|
194
|
-
def __init__(self, name: str, required: bool = False):
|
|
196
|
+
def __init__(self, name: str, required: bool, multiple: bool = False):
|
|
195
197
|
self.name = name
|
|
196
198
|
self.required = required
|
|
199
|
+
self.multiple = multiple
|
|
197
200
|
|
|
198
201
|
def to_dict(self):
|
|
199
202
|
return {
|
|
@@ -201,16 +204,17 @@ class Upload(Component):
|
|
|
201
204
|
'properties': {
|
|
202
205
|
'name': self.name,
|
|
203
206
|
'required': self.required,
|
|
207
|
+
'multiple': self.multiple,
|
|
204
208
|
},
|
|
205
209
|
}
|
|
206
210
|
|
|
207
|
-
|
|
208
211
|
class Button(Component):
|
|
209
|
-
def __init__(self, label: str, button_type: str = 'button', disabled: bool = False, on_click: Optional[str] = None):
|
|
212
|
+
def __init__(self, label: str, button_type: str = 'button', disabled: bool = False, on_click: Optional[str] = None, style: Optional[Dict[str, Any]] = None):
|
|
210
213
|
self.label = label
|
|
211
214
|
self.button_type = button_type
|
|
212
215
|
self.disabled = disabled
|
|
213
216
|
self.on_click = on_click
|
|
217
|
+
self.style = style
|
|
214
218
|
|
|
215
219
|
def to_dict(self):
|
|
216
220
|
return {
|
|
@@ -220,9 +224,74 @@ class Button(Component):
|
|
|
220
224
|
'type': self.button_type,
|
|
221
225
|
'disabled': self.disabled,
|
|
222
226
|
'onClick': self.on_click,
|
|
227
|
+
'style': self.style,
|
|
228
|
+
},
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
class Text(Component):
|
|
232
|
+
def __init__(self, content: Union[str, List[Dict[str, Any]]]):
|
|
233
|
+
self.content = content
|
|
234
|
+
|
|
235
|
+
def to_dict(self):
|
|
236
|
+
return {
|
|
237
|
+
'type': 'text',
|
|
238
|
+
'properties': {
|
|
239
|
+
'content': self.content,
|
|
223
240
|
},
|
|
224
241
|
}
|
|
225
242
|
|
|
243
|
+
class Select(Component):
|
|
244
|
+
def __init__(
|
|
245
|
+
self,
|
|
246
|
+
name: str,
|
|
247
|
+
options: List[Dict[str, Any]],
|
|
248
|
+
multiple: bool = False,
|
|
249
|
+
required: bool = False,
|
|
250
|
+
placeholder: Optional[str] = None,
|
|
251
|
+
label_field: Optional[str] = 'label',
|
|
252
|
+
value_field: Optional[str] = 'value',
|
|
253
|
+
on_change: Optional[str] = None,
|
|
254
|
+
):
|
|
255
|
+
self.name = name
|
|
256
|
+
self.options = options
|
|
257
|
+
self.multiple = multiple
|
|
258
|
+
self.required = required
|
|
259
|
+
self.placeholder = placeholder
|
|
260
|
+
self.value_field = value_field
|
|
261
|
+
self.label_field = label_field
|
|
262
|
+
self.on_change = on_change
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def to_dict(self):
|
|
266
|
+
return {
|
|
267
|
+
'type': 'select',
|
|
268
|
+
'properties': {
|
|
269
|
+
'name': self.name,
|
|
270
|
+
'options': self.options,
|
|
271
|
+
'multiple': self.multiple,
|
|
272
|
+
'required': self.required,
|
|
273
|
+
'placeholder': self.placeholder,
|
|
274
|
+
'labelField': self.label_field,
|
|
275
|
+
'valueField': self.value_field,
|
|
276
|
+
'onChange': self.on_change,
|
|
277
|
+
},
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
class Checkbox(Component):
|
|
281
|
+
def __init__(self, name: str, label: Optional[str] = None, checked: bool = False):
|
|
282
|
+
self.name = name
|
|
283
|
+
self.label = label
|
|
284
|
+
self.checked = checked
|
|
285
|
+
|
|
286
|
+
def to_dict(self):
|
|
287
|
+
return {
|
|
288
|
+
'type': 'checkbox',
|
|
289
|
+
'properties': {
|
|
290
|
+
'name': self.name,
|
|
291
|
+
'label': self.label,
|
|
292
|
+
'checked': self.checked,
|
|
293
|
+
},
|
|
294
|
+
}
|
|
226
295
|
|
|
227
296
|
class Tabs(Component):
|
|
228
297
|
def __init__(self, children: List['Tab']):
|
|
@@ -234,7 +303,6 @@ class Tabs(Component):
|
|
|
234
303
|
'children': [child.to_dict() for child in self.children],
|
|
235
304
|
}
|
|
236
305
|
|
|
237
|
-
|
|
238
306
|
class Tab(Component):
|
|
239
307
|
def __init__(self, label: str, children: List[Component]):
|
|
240
308
|
self.label = label
|
|
@@ -249,7 +317,6 @@ class Tab(Component):
|
|
|
249
317
|
'children': [child.to_dict() for child in self.children],
|
|
250
318
|
}
|
|
251
319
|
|
|
252
|
-
|
|
253
320
|
class ModalDialog(Component):
|
|
254
321
|
def __init__(
|
|
255
322
|
self,
|
|
@@ -259,7 +326,7 @@ class ModalDialog(Component):
|
|
|
259
326
|
visible: bool = False,
|
|
260
327
|
on_close: Optional[str] = None,
|
|
261
328
|
):
|
|
262
|
-
self.name = name
|
|
329
|
+
self.name = name
|
|
263
330
|
self.title = title
|
|
264
331
|
self.content = content
|
|
265
332
|
self.visible = visible
|
|
@@ -277,10 +344,11 @@ class ModalDialog(Component):
|
|
|
277
344
|
'children': [component.to_dict() for component in self.content],
|
|
278
345
|
}
|
|
279
346
|
|
|
280
|
-
|
|
281
347
|
class Page(Component):
|
|
282
|
-
def __init__(self, name: str, path: str, components: List[Component]):
|
|
348
|
+
def __init__(self, name: str, label: str, main: bool, path: str, components: List[Component]):
|
|
283
349
|
self.name = name
|
|
350
|
+
self.label = label
|
|
351
|
+
self.main = main
|
|
284
352
|
self.path = path
|
|
285
353
|
self.components = components
|
|
286
354
|
|
|
@@ -290,11 +358,12 @@ class Page(Component):
|
|
|
290
358
|
'properties': {
|
|
291
359
|
'name': self.name,
|
|
292
360
|
'path': self.path,
|
|
361
|
+
'label': self.label,
|
|
362
|
+
'main': self.main,
|
|
293
363
|
},
|
|
294
364
|
'children': [component.to_dict() for component in self.components],
|
|
295
365
|
}
|
|
296
366
|
|
|
297
|
-
|
|
298
367
|
class Action:
|
|
299
368
|
def __init__(
|
|
300
369
|
self,
|
|
@@ -302,15 +371,25 @@ class Action:
|
|
|
302
371
|
method: str,
|
|
303
372
|
url: Optional[str] = None,
|
|
304
373
|
data: Optional[Any] = None,
|
|
374
|
+
content_type: Optional[str] = None,
|
|
375
|
+
params: Optional[Dict[str, Any]] = None,
|
|
305
376
|
state: Optional[str] = None,
|
|
306
|
-
|
|
377
|
+
before_action: Optional[str] = None,
|
|
378
|
+
on_success: Optional[Any] = None,
|
|
379
|
+
on_error: Optional[Any] = None,
|
|
380
|
+
actions: Optional[List[Union['Action', str]]] = None,
|
|
307
381
|
):
|
|
308
382
|
self.action_type = action_type
|
|
309
383
|
self.method = method
|
|
310
384
|
self.url = url
|
|
311
385
|
self.data = data
|
|
386
|
+
self.content_type = content_type
|
|
387
|
+
self.params = params
|
|
312
388
|
self.state = state
|
|
389
|
+
self.before_action = before_action
|
|
313
390
|
self.on_success = on_success
|
|
391
|
+
self.on_error = on_error
|
|
392
|
+
self.actions = actions or []
|
|
314
393
|
|
|
315
394
|
def to_dict(self):
|
|
316
395
|
return {
|
|
@@ -318,8 +397,13 @@ class Action:
|
|
|
318
397
|
'method': self.method,
|
|
319
398
|
'url': self.url,
|
|
320
399
|
'data': self.data,
|
|
400
|
+
'contentType': self.content_type,
|
|
401
|
+
'params': self.params,
|
|
321
402
|
'state': self.state,
|
|
403
|
+
'beforeAction': self.before_action,
|
|
322
404
|
'onSuccess': self.on_success,
|
|
405
|
+
'onError': self.on_error,
|
|
406
|
+
'actions': [action.to_dict() if isinstance(action, Action) else action for action in self.actions],
|
|
323
407
|
}
|
|
324
408
|
|
|
325
409
|
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Global List of tools that can be used in the assistant
|
|
2
|
+
# Only functions marked with @assistant_tool will be available in the allowed list
|
|
3
|
+
# This is in addition to the tools from OpenAPI Spec add to allowed tools
|
|
4
|
+
# These tools are loaded in the agent like below
|
|
5
|
+
|
|
6
|
+
# async def load_global_tool_function():
|
|
7
|
+
# # Iterate over all modules in the package
|
|
8
|
+
# for loader, module_name, is_pkg in pkgutil.walk_packages(global_tools.__path__):
|
|
9
|
+
# module = importlib.import_module(f"{global_tools.__name__}.{module_name}")
|
|
10
|
+
# for name in dir(module):
|
|
11
|
+
# func = getattr(module, name)
|
|
12
|
+
# if callable(func) and getattr(func, 'is_assistant_tool', False):
|
|
13
|
+
# GLOBAL_TOOLS_FUNCTIONS[name] = func
|
|
14
|
+
|
|
15
|
+
# Global Data Models Used for Data Extraction. These can be referenced in workflows.
|
|
16
|
+
GLOBAL_DATA_MODELS = []
|
|
17
|
+
|
|
18
|
+
# Global Functions used in workflows. Like CRM, Email, etc. They can be invoked as Python functions.
|
|
19
|
+
GLOBAL_TOOLS_FUNCTIONS = {}
|
|
20
|
+
|
|
21
|
+
# GLOBAL_TOOLS_FUNCTIONS in OPENAI function spec format
|
|
22
|
+
# src/dhisana/utils/agent_tools.py
|
|
23
|
+
|
|
24
|
+
# Global List of tools that can be used in the assistant
|
|
25
|
+
# Only functions marked with @assistant_tool will be available in the allowed list
|
|
26
|
+
# This is in addition to the tools from OpenAPI Spec add to allowed tools
|
|
27
|
+
# These tools are loaded in the agent like below
|
|
28
|
+
|
|
29
|
+
# async def load_global_tool_function():
|
|
30
|
+
# # Iterate over all modules in the package
|
|
31
|
+
# for loader, module_name, is_pkg in pkgutil.walk_packages(global_tools.__path__):
|
|
32
|
+
# module = importlib.import_module(f"{global_tools.__name__}.{module_name}")
|
|
33
|
+
# for name in dir(module):
|
|
34
|
+
# func = getattr(module, name)
|
|
35
|
+
# if callable(func) and getattr(func, 'is_assistant_tool', False):
|
|
36
|
+
# GLOBAL_TOOLS_FUNCTIONS[name] = func
|
|
37
|
+
|
|
38
|
+
# Ensure GLOBAL_DATA_MODELS is only initialized once
|
|
39
|
+
if 'GLOBAL_DATA_MODELS' not in globals():
|
|
40
|
+
GLOBAL_DATA_MODELS = []
|
|
41
|
+
|
|
42
|
+
# Ensure GLOBAL_TOOLS_FUNCTIONS is only initialized once
|
|
43
|
+
if 'GLOBAL_TOOLS_FUNCTIONS' not in globals():
|
|
44
|
+
GLOBAL_TOOLS_FUNCTIONS = {}
|
|
45
|
+
|
|
46
|
+
# Ensure GLOBAL_OPENAI_ASSISTANT_TOOLS is only initialized once
|
|
47
|
+
if 'GLOBAL_OPENAI_ASSISTANT_TOOLS' not in globals():
|
|
48
|
+
GLOBAL_OPENAI_ASSISTANT_TOOLS = []
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import aiohttp
|
|
3
|
+
from dhisana.utils.assistant_tool_tag import assistant_tool
|
|
4
|
+
from typing import List, Optional, Union
|
|
5
|
+
import backoff
|
|
6
|
+
|
|
7
|
+
@assistant_tool
|
|
8
|
+
@backoff.on_exception(
|
|
9
|
+
backoff.expo,
|
|
10
|
+
aiohttp.ClientResponseError,
|
|
11
|
+
max_tries=2,
|
|
12
|
+
giveup=lambda e: e.status != 429,
|
|
13
|
+
factor=10,
|
|
14
|
+
)
|
|
15
|
+
async def enrich_person_info_from_apollo(
|
|
16
|
+
linkedin_url: Optional[str] = None,
|
|
17
|
+
email: Optional[str] = None,
|
|
18
|
+
phone: Optional[str] = None,
|
|
19
|
+
):
|
|
20
|
+
"""
|
|
21
|
+
Fetch a person's details from Apollo using LinkedIn URL, email, or phone number.
|
|
22
|
+
|
|
23
|
+
Parameters:
|
|
24
|
+
- **linkedin_url** (*str*, optional): LinkedIn profile URL of the person.
|
|
25
|
+
- **email** (*str*, optional): Email address of the person.
|
|
26
|
+
- **phone** (*str*, optional): Phone number of the person.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
- **dict**: JSON response containing person information.
|
|
30
|
+
"""
|
|
31
|
+
APOLLO_API_KEY = os.environ.get('APOLLO_API_KEY')
|
|
32
|
+
if not APOLLO_API_KEY:
|
|
33
|
+
return {'error': "Apollo API key not found in environment variables"}
|
|
34
|
+
|
|
35
|
+
if not linkedin_url and not email and not phone:
|
|
36
|
+
return {'error': "At least one of linkedin_url, email, or phone must be provided"}
|
|
37
|
+
|
|
38
|
+
headers = {
|
|
39
|
+
"X-Api-Key": f"{APOLLO_API_KEY}",
|
|
40
|
+
"Content-Type": "application/json"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
data = {}
|
|
44
|
+
if linkedin_url:
|
|
45
|
+
data['linkedin_url'] = linkedin_url
|
|
46
|
+
if email:
|
|
47
|
+
data['email'] = email
|
|
48
|
+
if phone:
|
|
49
|
+
data['phone_numbers'] = [phone] # Apollo expects a list for phone numbers
|
|
50
|
+
|
|
51
|
+
url = 'https://api.apollo.io/v1/people/match' # Correct API endpoint
|
|
52
|
+
|
|
53
|
+
async with aiohttp.ClientSession() as session:
|
|
54
|
+
async with session.post(url, headers=headers, json=data) as response:
|
|
55
|
+
if response.status == 200:
|
|
56
|
+
return await response.json()
|
|
57
|
+
elif response.status == 429:
|
|
58
|
+
raise aiohttp.ClientResponseError(
|
|
59
|
+
request_info=response.request_info,
|
|
60
|
+
history=response.history,
|
|
61
|
+
status=response.status,
|
|
62
|
+
message="Rate limit exceeded",
|
|
63
|
+
headers=response.headers
|
|
64
|
+
)
|
|
65
|
+
else:
|
|
66
|
+
result = await response.json()
|
|
67
|
+
return {'error': result}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@assistant_tool
|
|
72
|
+
@backoff.on_exception(
|
|
73
|
+
backoff.expo,
|
|
74
|
+
aiohttp.ClientResponseError,
|
|
75
|
+
max_tries=2,
|
|
76
|
+
giveup=lambda e: e.status != 429,
|
|
77
|
+
factor=10,
|
|
78
|
+
)
|
|
79
|
+
async def enrich_company_info_from_apollo(
|
|
80
|
+
company_domain: Optional[str] = None,
|
|
81
|
+
):
|
|
82
|
+
"""
|
|
83
|
+
Fetch a company's details from Apollo using the company domain.
|
|
84
|
+
|
|
85
|
+
Parameters:
|
|
86
|
+
- **company_domain** (*str*, optional): Domain of the company.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
- **dict**: JSON response containing company information.
|
|
90
|
+
"""
|
|
91
|
+
APOLLO_API_KEY = os.environ.get('APOLLO_API_KEY')
|
|
92
|
+
if not APOLLO_API_KEY:
|
|
93
|
+
return {'error': "Apollo API key not found in environment variables"}
|
|
94
|
+
|
|
95
|
+
if not company_domain:
|
|
96
|
+
return {'error': "Company domain must be provided"}
|
|
97
|
+
|
|
98
|
+
headers = {
|
|
99
|
+
"X-Api-Key": f"{APOLLO_API_KEY}",
|
|
100
|
+
"Content-Type": "application/json",
|
|
101
|
+
"Cache-Control": "no-cache",
|
|
102
|
+
"accept": "application/json"
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
url = f'https://api.apollo.io/api/v1/organizations/enrich?domain={company_domain}'
|
|
106
|
+
|
|
107
|
+
async with aiohttp.ClientSession() as session:
|
|
108
|
+
async with session.get(url, headers=headers) as response:
|
|
109
|
+
if response.status == 200:
|
|
110
|
+
return await response.json()
|
|
111
|
+
elif response.status == 429:
|
|
112
|
+
raise aiohttp.ClientResponseError(
|
|
113
|
+
request_info=response.request_info,
|
|
114
|
+
history=response.history,
|
|
115
|
+
status=response.status,
|
|
116
|
+
message="Rate limit exceeded",
|
|
117
|
+
headers=response.headers
|
|
118
|
+
)
|
|
119
|
+
else:
|
|
120
|
+
result = await response.json()
|
|
121
|
+
return {'error': result}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Check validity of email
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import os
|
|
5
|
+
import aiohttp
|
|
6
|
+
from dhisana.utils.assistant_tool_tag import assistant_tool
|
|
7
|
+
|
|
8
|
+
@assistant_tool
|
|
9
|
+
async def check_email_validity_with_zero_bounce(email_id: str):
|
|
10
|
+
"""
|
|
11
|
+
Validate a single email address using the ZeroBounce API.
|
|
12
|
+
|
|
13
|
+
This function sends an asynchronous GET request to the ZeroBounce API to validate the provided email address.
|
|
14
|
+
|
|
15
|
+
Parameters:
|
|
16
|
+
email_id (str): The email address to be validated.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
dict: The JSON response from the ZeroBounce API containing the validation results.
|
|
20
|
+
|
|
21
|
+
Raises:
|
|
22
|
+
ValueError: If the ZeroBounce API key is not found in the environment variables.
|
|
23
|
+
Exception: If the response status code from the ZeroBounce API is not 200.
|
|
24
|
+
"""
|
|
25
|
+
ZERO_BOUNCE_API_KEY = os.environ.get('ZERO_BOUNCE_API_KEY')
|
|
26
|
+
if not ZERO_BOUNCE_API_KEY:
|
|
27
|
+
raise ValueError("ZeroBounce API key not found in environment variables")
|
|
28
|
+
|
|
29
|
+
url = f"https://api.zerobounce.net/v2/validate?api_key={ZERO_BOUNCE_API_KEY}&email={email_id}"
|
|
30
|
+
|
|
31
|
+
async with aiohttp.ClientSession() as session:
|
|
32
|
+
async with session.get(url) as response:
|
|
33
|
+
if response.status != 200:
|
|
34
|
+
raise Exception(f"Error: Received status code {response.status}")
|
|
35
|
+
result = await response.json()
|
|
36
|
+
return result
|